From e2b53c793633c09ae8c3995e6f11bf9e7ed03d93 Mon Sep 17 00:00:00 2001 From: aoshiguchen <1052045476@qq.com> Date: Fri, 15 Jul 2022 21:33:57 +0800 Subject: [PATCH] =?UTF-8?q?web=E6=9C=8D=E5=8A=A1=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E6=9B=B4=E6=96=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/context/ApplicationContext.java | 6 +- .../core/context/ExtensionServiceLoader.java | 10 ++- .../asgc/neutrino/core/web/HttpMethod.java | 40 ++++++++++ .../neutrino/core/web/HttpRequestHandler.java | 47 ++++++++++++ .../core/web/WebApplicationContext.java | 70 ++++++++++++++++++ .../core/web/WebApplicationServer.java | 32 +++++--- .../core/web/annotation/GetMapping.java | 38 ++++++++++ .../core/web/annotation/PostMapping.java | 38 ++++++++++ .../core/web/annotation/RestController.java | 42 +++++++++++ .../core/web/exception/WebException.java | 45 +++++++++++ .../WebResourceNotFoundException.java | 38 ++++++++++ .../asgc/neutrino/core/util/LockUtilTest.java | 6 +- .../neutrino/core/web/HttpMethodTest.java | 38 ++++++++++ .../src/main/resources/static/favicon.ico | Bin 0 -> 67646 bytes 14 files changed, 429 insertions(+), 21 deletions(-) create mode 100644 neutrino-core/src/main/java/fun/asgc/neutrino/core/web/HttpMethod.java create mode 100644 neutrino-core/src/main/java/fun/asgc/neutrino/core/web/HttpRequestHandler.java create mode 100644 neutrino-core/src/main/java/fun/asgc/neutrino/core/web/WebApplicationContext.java create mode 100644 neutrino-core/src/main/java/fun/asgc/neutrino/core/web/annotation/GetMapping.java create mode 100644 neutrino-core/src/main/java/fun/asgc/neutrino/core/web/annotation/PostMapping.java create mode 100644 neutrino-core/src/main/java/fun/asgc/neutrino/core/web/annotation/RestController.java create mode 100644 neutrino-core/src/main/java/fun/asgc/neutrino/core/web/exception/WebException.java create mode 100644 neutrino-core/src/main/java/fun/asgc/neutrino/core/web/exception/WebResourceNotFoundException.java create mode 100644 neutrino-core/src/test/java/fun/asgc/neutrino/core/web/HttpMethodTest.java create mode 100644 neutrino-proxy-server/src/main/resources/static/favicon.ico diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/context/ApplicationContext.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/context/ApplicationContext.java index 86246b5b..68b36880 100644 --- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/context/ApplicationContext.java +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/context/ApplicationContext.java @@ -74,9 +74,9 @@ public class ApplicationContext implements LifeCycle { GlobalConfig.setIsContainerStartup(true); this.rootBeanFactory = new SimpleBeanFactory("rootBeanFactory"); this.applicationBeanFactory = new SimpleBeanFactory(rootBeanFactory, "applicationBeanFactory"); - this.rootBeanFactory.registerBean(environment, "rootEnvironment"); - this.rootBeanFactory.registerBean(this, "rootApplicationContext"); - this.rootBeanFactory.registerBean(environment.getConfig(), "rootApplicationConfig"); + this.rootBeanFactory.registerBean(environment); + this.rootBeanFactory.registerBean(this); + this.rootBeanFactory.registerBean(environment.getConfig()); register(); List beanFactoryAwareList = this.applicationBeanFactory.getBeanList(BeanFactoryAware.class); if (CollectionUtil.notEmpty(beanFactoryAwareList)) { diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/context/ExtensionServiceLoader.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/context/ExtensionServiceLoader.java index d6a030c0..43deca4c 100644 --- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/context/ExtensionServiceLoader.java +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/context/ExtensionServiceLoader.java @@ -25,6 +25,8 @@ import fun.asgc.neutrino.core.annotation.Autowired; import fun.asgc.neutrino.core.annotation.Component; import fun.asgc.neutrino.core.annotation.NonIntercept; import fun.asgc.neutrino.core.bean.SimpleBeanFactory; +import fun.asgc.neutrino.core.web.HttpRequestHandler; +import fun.asgc.neutrino.core.web.WebApplicationContext; import fun.asgc.neutrino.core.web.WebApplicationServer; /** @@ -36,7 +38,7 @@ import fun.asgc.neutrino.core.web.WebApplicationServer; @Component public class ExtensionServiceLoader implements ApplicationRunner { @Autowired - private ApplicationConfig rootApplicationConfig; + private ApplicationConfig applicationConfig; @Autowired private SimpleBeanFactory applicationBeanFactory; @@ -46,13 +48,13 @@ public class ExtensionServiceLoader implements ApplicationRunner { } private void startHttpServer() { - if (null == rootApplicationConfig) { + if (null == applicationConfig) { return; } - ApplicationConfig.Http http = rootApplicationConfig.getHttp(); + ApplicationConfig.Http http = applicationConfig.getHttp(); if (null == http || null == http.getEnable() || !http.getEnable()) { return; } - applicationBeanFactory.registerBean(WebApplicationServer.class); + applicationBeanFactory.registerBean(WebApplicationContext.class); } } diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/HttpMethod.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/HttpMethod.java new file mode 100644 index 00000000..8b97c6e3 --- /dev/null +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/HttpMethod.java @@ -0,0 +1,40 @@ +/** + * Copyright (c) 2022 aoshiguchen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package fun.asgc.neutrino.core.web; + +import fun.asgc.neutrino.core.util.StringUtil; + +/** + * + * @author: aoshiguchen + * @date: 2022/7/15 + */ +public enum HttpMethod { + GET,POST; + + public static HttpMethod of(String method) { + if (StringUtil.isEmpty(method)) { + return null; + } + return HttpMethod.valueOf(method.toUpperCase()); + } +} diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/HttpRequestHandler.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/HttpRequestHandler.java new file mode 100644 index 00000000..3ddb09fe --- /dev/null +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/HttpRequestHandler.java @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2022 aoshiguchen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package fun.asgc.neutrino.core.web; + +import fun.asgc.neutrino.core.annotation.Autowired; +import fun.asgc.neutrino.core.annotation.Component; +import fun.asgc.neutrino.core.annotation.NonIntercept; +import fun.asgc.neutrino.core.context.ApplicationConfig; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.FullHttpRequest; + +/** + * + * @author: aoshiguchen + * @date: 2022/7/15 + */ +@NonIntercept +@Component +public class HttpRequestHandler { + @Autowired + private ApplicationConfig applicationConfig; + + public void handle(ChannelHandlerContext context, FullHttpRequest request) { + // TODO + System.out.println("hello"); + } + +} diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/WebApplicationContext.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/WebApplicationContext.java new file mode 100644 index 00000000..fb62f99e --- /dev/null +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/WebApplicationContext.java @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2022 aoshiguchen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package fun.asgc.neutrino.core.web; + +import fun.asgc.neutrino.core.annotation.*; +import fun.asgc.neutrino.core.bean.SimpleBeanFactory; +import fun.asgc.neutrino.core.context.ApplicationContext; +import fun.asgc.neutrino.core.context.ApplicationRunner; +import lombok.Data; +import lombok.experimental.Accessors; +import lombok.extern.slf4j.Slf4j; + +/** + * + * @author: aoshiguchen + * @date: 2022/7/15 + */ +@Accessors(chain = true) +@Data +@Slf4j +@NonIntercept +@Component +public class WebApplicationContext implements ApplicationRunner { + @Autowired + private ApplicationContext applicationContext; + @Autowired + private SimpleBeanFactory applicationBeanFactory; + /** + * bean工厂 + */ + private SimpleBeanFactory webApplicationBeanFactory; + + @Init + public void init() { + this.webApplicationBeanFactory = new SimpleBeanFactory(applicationBeanFactory, "webApplicationBeanFactory"); + this.webApplicationBeanFactory.init(); + // TODO 初始化路由信息 + } + + @Destroy + public void destroy() { + this.webApplicationBeanFactory.destroy(); + } + + @Override + public void run(String[] args) throws Exception { + this.webApplicationBeanFactory.registerBean(HttpRequestHandler.class); + this.webApplicationBeanFactory.registerBean(WebApplicationServer.class); + } + +} diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/WebApplicationServer.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/WebApplicationServer.java index bb3b7a88..21433e1b 100644 --- a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/WebApplicationServer.java +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/WebApplicationServer.java @@ -27,9 +27,7 @@ import fun.asgc.neutrino.core.annotation.Destroy; import fun.asgc.neutrino.core.annotation.Init; import fun.asgc.neutrino.core.context.ApplicationConfig; import fun.asgc.neutrino.core.context.ApplicationRunner; -import fun.asgc.neutrino.core.util.HttpServerUtil; -import fun.asgc.neutrino.core.util.NumberUtil; -import fun.asgc.neutrino.core.util.StringUtil; +import fun.asgc.neutrino.core.util.*; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.Unpooled; import io.netty.channel.*; @@ -49,7 +47,9 @@ import lombok.extern.slf4j.Slf4j; @Component public class WebApplicationServer implements ApplicationRunner { @Autowired - private ApplicationConfig rootApplicationConfig; + private ApplicationConfig applicationConfig; + @Autowired + private HttpRequestHandler httpRequestHandler; private EventLoopGroup bossGroup; private EventLoopGroup workerGroup; private ServerBootstrap serverBootstrap; @@ -58,7 +58,7 @@ public class WebApplicationServer implements ApplicationRunner { @Init public void init() { - ApplicationConfig.Http http = rootApplicationConfig.getHttp(); + ApplicationConfig.Http http = applicationConfig.getHttp(); if (StringUtil.notEmpty(http.getMaxContentLengthDesc()) && null == http.getMaxContentLength()) { http.setMaxContentLength(NumberUtil.descriptionToSize(http.getMaxContentLengthDesc(), 64 * 1024)); } @@ -66,14 +66,12 @@ public class WebApplicationServer implements ApplicationRunner { this.bossGroup = new NioEventLoopGroup(); this.workerGroup = new NioEventLoopGroup(); this.serverBootstrap = new ServerBootstrap(); + this.initFavicon(); } @Override public void run(String[] args) throws Exception { - ApplicationConfig.Http http = rootApplicationConfig.getHttp(); - if (!http.getContextPath().endsWith("/")) { - http.setContextPath(http.getContextPath() + "/"); - } + ApplicationConfig.Http http = applicationConfig.getHttp(); this.serverBootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) @@ -93,8 +91,8 @@ public class WebApplicationServer implements ApplicationRunner { protected void channelRead0(ChannelHandlerContext context, FullHttpRequest request) throws Exception { String uri = request.uri(); log.debug("http request: {}", uri); - if (uri.startsWith(http.getContextPath())) { - // TODO + if (uri.startsWith(http.getContextPath() + "/") || uri.equals(http.getContextPath())) { + httpRequestHandler.handle(context, request); } else if (uri.equals("/favicon.ico") && null != faviconBytes) { FullHttpResponse fullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer(faviconBytes)); fullHttpResponse.headers().add(HttpHeaderNames.CONTENT_TYPE, "image/x-icon"); @@ -110,6 +108,18 @@ public class WebApplicationServer implements ApplicationRunner { log.info("HTTP服务启动,端口:{} context-path:{}", http.getPort(), http.getContextPath()); } + private void initFavicon() { + if (null == applicationConfig.getHttp().getStaticResource() || CollectionUtil.isEmpty(applicationConfig.getHttp().getStaticResource().getLocations())) { + return; + } + for (String location : applicationConfig.getHttp().getStaticResource().getLocations()) { + this.faviconBytes = FileUtil.readBytes(location.concat("favicon.ico")); + if (null != faviconBytes) { + break; + } + } + } + @Destroy public void destroy() { this.channelFuture.channel().close(); diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/annotation/GetMapping.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/annotation/GetMapping.java new file mode 100644 index 00000000..b7c065f6 --- /dev/null +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/annotation/GetMapping.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2022 aoshiguchen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package fun.asgc.neutrino.core.web.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author: aoshiguchen + * @date: 2022/7/15 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE,ElementType.METHOD}) +public @interface GetMapping { + String[] value() default {}; +} diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/annotation/PostMapping.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/annotation/PostMapping.java new file mode 100644 index 00000000..4f092a1d --- /dev/null +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/annotation/PostMapping.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2022 aoshiguchen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package fun.asgc.neutrino.core.web.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author: aoshiguchen + * @date: 2022/7/15 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE,ElementType.METHOD}) +public @interface PostMapping { + String[] value() default {}; +} diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/annotation/RestController.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/annotation/RestController.java new file mode 100644 index 00000000..7e6f301a --- /dev/null +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/annotation/RestController.java @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2022 aoshiguchen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package fun.asgc.neutrino.core.web.annotation; + +import fun.asgc.neutrino.core.annotation.Component; +import fun.asgc.neutrino.core.annotation.NonIntercept; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author: aoshiguchen + * @date: 2022/7/15 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +@Component +@NonIntercept +public @interface RestController { + +} diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/exception/WebException.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/exception/WebException.java new file mode 100644 index 00000000..430c24d6 --- /dev/null +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/exception/WebException.java @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2022 aoshiguchen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package fun.asgc.neutrino.core.web.exception; + +import fun.asgc.neutrino.core.exception.InternalException; + +/** + * web服务异常 + * @author: aoshiguchen + * @date: 2022/7/15 + */ +public class WebException extends InternalException { + protected String method; + protected Integer code; + private String path; + protected String message; + + public WebException(String method, Integer code, String path, String message) { + super(message); + } + + public WebException(String method, Integer code, String path, String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/exception/WebResourceNotFoundException.java b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/exception/WebResourceNotFoundException.java new file mode 100644 index 00000000..0821de7c --- /dev/null +++ b/neutrino-core/src/main/java/fun/asgc/neutrino/core/web/exception/WebResourceNotFoundException.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2022 aoshiguchen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package fun.asgc.neutrino.core.web.exception; + +/** + * web资源找不到异常 + * @author: aoshiguchen + * @date: 2022/7/15 + */ +public class WebResourceNotFoundException extends WebException { + + public WebResourceNotFoundException(String method, String path, String message) { + super(method, 404, path, message); + } + + public WebResourceNotFoundException(String method, String path, String message, Throwable cause) { + super(method, 404, path, message, cause); + } +} diff --git a/neutrino-core/src/test/java/fun/asgc/neutrino/core/util/LockUtilTest.java b/neutrino-core/src/test/java/fun/asgc/neutrino/core/util/LockUtilTest.java index e93b74f5..8a03d78f 100644 --- a/neutrino-core/src/test/java/fun/asgc/neutrino/core/util/LockUtilTest.java +++ b/neutrino-core/src/test/java/fun/asgc/neutrino/core/util/LockUtilTest.java @@ -70,8 +70,8 @@ public class LockUtilTest { @Data public static class A { - public A() { - System.out.println("new instance."); - } +// public A() { +// System.out.println("new instance."); +// } } } diff --git a/neutrino-core/src/test/java/fun/asgc/neutrino/core/web/HttpMethodTest.java b/neutrino-core/src/test/java/fun/asgc/neutrino/core/web/HttpMethodTest.java new file mode 100644 index 00000000..40b33cfe --- /dev/null +++ b/neutrino-core/src/test/java/fun/asgc/neutrino/core/web/HttpMethodTest.java @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2022 aoshiguchen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package fun.asgc.neutrino.core.web; + +import org.junit.Test; + +/** + * + * @author: aoshiguchen + * @date: 2022/7/15 + */ +public class HttpMethodTest { + + @Test + public void test1() { + HttpMethod method = HttpMethod.of("Get"); + System.out.println(method); + } +} diff --git a/neutrino-proxy-server/src/main/resources/static/favicon.ico b/neutrino-proxy-server/src/main/resources/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..474afb8c073f4683386b03ca8782e6a3d909c54a GIT binary patch literal 67646 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

7M}6UZmiWESP zGpUZONQ#ghDOC|4b>$*{XC?0&`=xcxerr3GL+rS}F+T5J+(X_r^q|hc9?}UpG`@B) zaxVrv)r0>n;5F~Fow>&T`Z(qt*H_**g7?_QzPvxAvL9lg=1a0U59Qm=X`DZ;bN1gq zEJ}t}#ouOy{5QexJ>ko@4djtrjjvr?PK>Yp5)X=d+-E!cvA-iWcE#ACRq!{J83Qxx zTjGncrh_rZa}|5$8lcu4p4;?e+(6FZQMHs@#+oB~Ide>}5D$6V&=t?&6Y7|`Coeh| zIMTn23AVFeR&7g$jA6*v10&2aV3;d)UUTigIJXJjBea)mD>}=S#fkFm>_&(I#DUG2 zi_mfB!0OVwbTPzc(Jbqj6&rhpc*{lj&+mjRO$VI%b`4+!BR0pqlpY80Y z>@tsY$VPom#K5SUf!NE(Vou9^+PwAT@`9G~!_MjQ`=ved^n1Kl{^5c=xqVU|oZBk= z4Ef@$Myf{Xdh$p)%M!|3^n6V`^ns0U3%F066pNpe1;>|WHBkJMbAs2r&vy26mQ8)( zpYD)1tw9OoGI=x13m&iGd*i=-^QAod<)%D)a9f`JdMg#rasAg%m*uBJOXMr)eAWiq&DKlXQ) zO_bFW&v~EqttOXI&n@#dkShyX$wP4e{K0qNoZP1Y#sp&H-rl*W0~Vc?99++98m86& zXX$INoc<+?NsgjK2J;!)^mF*1`?tw;@w+i5)|E@-fPOTC*Syd6p2(yA=&$V2$7j(# z=KtXG_?=~lFKuq~{HO!0`Om>Q`Ogp!@5ocE2{%@C)AAqd0rkH;r;%*yjqiSiQtu4> zl0_Rb;x`4d-(;{hPN@g(C)I=h?P6Mw`O{^zVz&-nAhaKd9LQHY_dIn?8GWX?q z&9L6@l^4I=mFEw?&lK+Mou}0ggZ>9c#z_A%UMgm@L;qLF98jv5&p@+I>@&4q2{{e!FVAVL_$TKCuX%qeVuSs%%Z9#?J*!bEtGO?Mziars z@af$P^8B|S0K~v6@Z$F$<-ysla>-!FX`yI+x zHW=n~mQDR2e@^2ttNAa@ijx~Fy2zjRF3NN8s>J}~Abq@ebWa|B{e{{G>bB4h)~}%z z{8ZdKOJ{rJ3Urcw{$tQ<@h+{e>q_!fP? zo)MP@_e~V{O)JjJG%kUa4|)za1V`NAZ3I{<_Ru&IH)kw*M9Q>g<$; zG85c0u7)CRHJ$aNvuv=IqqvClkt1VbwfNQbNA3P@;~@F#=bI`9nBVE*@z3(;8op1y zBvFeYvy7|ro8V``DoIKy*yA(~to6@WuGing%Xq&4#F=278C^9%j*O0#YxA2bE_F_| zW4zUx&UKKr(7KNn1H8vPA@{SeCUB25%*pz`%688AahJ+3+cK}I+~2=Yt@|$?|AIL9 zS&IeK1l9#!|NZz^dHUl;#L7U}Y;7ZJ;`F*DWH98vN{4J}e=#E5`#B)a0`(kR)?2>l zkMCR0ZzfmZBP)RC>-gT?oh?JzRG*WJ!_g_EPbf+>n&qs!PKN}hDBhJDQ4gPHTH2?G?96YOUsb3VrD(Pou+T*>_$u@1~#f1V6=(n&~BGd6~t86Mu|I?-%x9i!cvOa$S;w%h{@WFT8 zYs>Wo&E+a${OWuhVf(d(t>ovg)?kl>b@J(N^5V%u;DH%9$33>O5Bn;AaV%s|7G+Yl zv-GrA4)bm#p40XZtBn|?UIzPUCv#ipS=nrJt@`WZn&Uk{Q(2x6DTA^18|g&<+iBOI zqa%Et0FqUVfegm)wAvx4*G*9SEw+-&A9u}^=UgAj{ZsOt77zpI!@d^&n=4tAN!gr( zb7fUud&hIm5#VZKjUYKPs;<(BHX5PDgvD-SoAI3eu3_D{IJ>E=NGONBANEU#f!AQy zld%y#j{tE7hQp3kZOS2ETPpjm&2ORnb7Of2d3@)*{Qc)6GylKim==Cheb|>{G@n5R zd_|d*%{iQ*tD`bF|Fk-xh;goGv@a8&{tH{e2J61OzBH$qtfXD$xOP-82gf}c7wP)| z5NCija(mAja$`{|_=Yh6AlC109fiHoFN#~nz^lN%9HaO*%b`rl<{Zw@)lnIoe`TBU z7W>TlDSokTX-9AkzNzzd;37EQH?$7k^)*uWQ+-(eIjHy5{tUGxRw0Q}#_=N^Cm`G;NL7>?EDP$p$_4rl1US~ z4=}b8{vPYN1{$|!8>!=k8BOHyNc{e|iE(&G)fnGFYKT2c9ogKaip;53QbwT0PRIJi z7*KIx7|ZdO=Qfwsh=Gxa0dnVveh!SeKcSrOZ9tp>uBp2R#8~r>8h3y1eEIvY$992Z zRNXiGiTqPG=WvFu_R5$DAB_e#%Uec3FD;aB_45QkxQT5 z#60|d;rgUXO8%7Wv3upTO8yf7aR#PC4t=$v1^%8Ve%}&pupmKhElZN0_Aij9_pX@Z z;2C1!nL6h^wy_WM!7{FmlC`R3q>{}!oS|!`Wgt!_BKNuXytORRN-x%=1@%kGWVAb~ z7yUIKb?xkwCde6U`HYFH3tGzt!~k`e23;JTCmnky%&6qw8F&szRzck+qF#~zYYST| zduUTCR6HcecdL`-$ImCo{~cJQz;oUs@3+uL^~YzWJlrZFU@Uf(F?j!uYH5@eRH&DRWHWR0>r?%sZEsJTeMvVd~j`Hf^2MG33~x8&g``z zo&My!^UTTtwSY%}WEHeyMe_)}?`Wynv9v9GmKJCqa-8b|z;i7|tozc(XC^n1IapsA z`&re`k?~x6zwBM>C40!pviNYUM}AJxYYO&x3sFnXO>O$pT=avAfew|`USLM0taAll zkn>uzDhK!jcV^+4)ovhW)EFGOw^#}8FU@HM{oAF(yEVUQ$A;GD_VRX$`$h1{G{{cX zO`p>deKR}8JO}e;Zf(BnWjtGKqHae=HIQj|)@MxVK1kIuv-7Z@VLW4r_G&R;nU8+r z{MQz?kxht!DHXMtOPyDjVc)!W$o=dp0sjGx0^%sh&#D%YsFAJZ_VOgGlhEA^`s&)E zMC_YjgH4;>$9QBP_B}YXp3H;qr^8l9^~&lv+O@q~jhFPMersBl%NE^e@8Slf@Lhvu zFU_m_a&w0&sE5c2@SX)5$vL^tHK%IeVn7@L^<8bnJ^9x_E*TT_6>)4#oQh%I|L&vq zR^UCxH~XwhC@(WqZumQ-lOuN6j+qVL&aG7(-}`HB=0UURJLq|8LcGkX79?|^qwP3n zT+e_{mo&idFrH>TH~mUKT}M7`=~zYWJF=j|VsbyPYM^Ou)xZvb;QW=)`|-&7xTa+} zJnv&q2R;?A&`;-=_u0-EnTfu$FlJe!(((y(-;+{X4h^fPz;oVX8~d=YJ`Z&=o@Yi! zXY?TlpLDD$@tf$qeS9AN5-cZm)pjJ0Ep`kC4x1@S6A6A?MkT{VA76 z#_KHH|EFE6 zEBjyekNxwcFGIIo^Yurliz)_I=y_)G^-cU7!nRJ;R34FkO(u`}xlu3u9=NU+_%Cw5 zD5|(=Vf7%Q7a*^~9IT7$Tb0+iU(peDAG(-v2iJ@VwzJ=>WW9;)3$U)wgC9T*0Q)x37L;No0GcFMO|FM7?{!0N+;@e2QhJTNs?^qTus$a>YoYS$hG1g@nTw3 zL%{>b>wu&O>an3Uxlc?(kB<0GTpQIs7r!$lJsEHM{dur)J!;!6{B1a$d(F=sWmlhQ zd0novdu6eIH>@z7RF`b=fi>+^&i%gWprz z>LvZi!>y(5<1?g8UCQEv*Se1t$Y9a4tAv{}v#d!0mN#d>5a| zYkuLpozyk&vz`6eUmvHAl<}s+pfmTXM~2pyyQ>U#!pAokC&}6-;j$RD;kDUk(35ex zrg1oamq6<@Ik~;GgM8klhT3}@IC+8dWazKBCg;X~XfrKC8}I?J4Uk3PbaNYUzp$O= z6Y_7y-Bn%GeYP(|KlXo{v7|@`W7l7V~9q3L@$*tnw$i05fw&fZ^7FQ3tzNJk?aGaEed~%;8 zY-dg^$Mf(xRnj^!VrfrQrX{>;#jD>g4{wIzp0OTYq?zbgYfU(sr+WW`=XyJ{V8_)mrdb`zmoOY6f4YFV?&xSk3yypXHm8An+Hs*YM+-ZmJ%v zMqOZ^x5-{|w5fSyoXCq@*bgZFYDdKrNs%FdkSAd=4A~z5#9m_kmx5 z-+?Eh*KI&|12W_K` zw3Rl~cKU$6pik%<`e-~rpV4>p;lJrqTR-zUtN%od67bgVsqwmy$OGf`2O@io*Z&Y1 zYP`;6EzV?;zm3f%(~#%Z*X6CIGnoomO=mKFaMQZ~|JYvdwQm2%Q0wblue!GG{{HH{ z2i|+&y$9ZV;J?KKL#+*c<$AH#y8l0JT3`Q>K#~6sXjV