From 4feea0e7847ca23167a3a519b4f48b9b06507479 Mon Sep 17 00:00:00 2001 From: Redkale <22250530@qq.com> Date: Fri, 12 May 2017 13:42:10 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84HttpServlet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/org/redkale/boot/ApiDocsService.java | 15 +- src/org/redkale/net/Response.java | 12 + .../redkale/net/http/DefaultRestServlet.java | 5 - src/org/redkale/net/http/HttpBaseServlet.java | 69 ++-- src/org/redkale/net/http/HttpCacheable.java | 33 ++ src/org/redkale/net/http/HttpContext.java | 113 ++++++ src/org/redkale/net/http/HttpMapping.java | 39 ++ src/org/redkale/net/http/HttpParam.java | 61 ++++ .../redkale/net/http/HttpPrepareServlet.java | 12 +- src/org/redkale/net/http/HttpRequest.java | 24 ++ src/org/redkale/net/http/HttpResponse.java | 55 ++- src/org/redkale/net/http/HttpResult.java | 103 ++++++ src/org/redkale/net/http/HttpServlet.java | 344 +++++++++++++++++- src/org/redkale/net/http/Rest.java | 24 +- src/org/redkale/net/http/RestHttpServlet.java | 2 +- src/org/redkale/net/http/RestMapping.java | 6 +- src/org/redkale/net/http/RestOutput.java | 3 + .../redkale/net/http/WebSocketServlet.java | 6 +- src/org/redkale/util/AuthIgnore.java | 27 ++ .../redkale/test/rest/SimpleRestServlet.java | 6 +- .../test/rest/_DynHelloRestServlet1.java | 16 +- .../test/rest/_DynHelloRestServlet2.java | 22 +- 22 files changed, 891 insertions(+), 106 deletions(-) create mode 100644 src/org/redkale/net/http/HttpCacheable.java create mode 100644 src/org/redkale/net/http/HttpMapping.java create mode 100644 src/org/redkale/net/http/HttpParam.java create mode 100644 src/org/redkale/net/http/HttpResult.java create mode 100644 src/org/redkale/util/AuthIgnore.java diff --git a/src/org/redkale/boot/ApiDocsService.java b/src/org/redkale/boot/ApiDocsService.java index 6bd4b247f..36c10b141 100644 --- a/src/org/redkale/boot/ApiDocsService.java +++ b/src/org/redkale/boot/ApiDocsService.java @@ -11,7 +11,7 @@ import java.util.*; import javax.persistence.*; import org.redkale.convert.json.JsonConvert; import org.redkale.net.http.*; -import org.redkale.service.Service; +import org.redkale.service.*; import org.redkale.source.*; import org.redkale.util.*; @@ -24,7 +24,9 @@ import org.redkale.util.*; * * @author zhangjx */ -public class ApiDocsService extends DefaultRestServlet implements Service { +@AutoLoad(false) +@LocalService +public final class ApiDocsService extends AbstractService { private final Application app; //Application全局对象 @@ -34,7 +36,8 @@ public class ApiDocsService extends DefaultRestServlet implements Service { public void run() throws Exception { List serverList = new ArrayList<>(); - + Field __prefix = HttpServlet.class.getDeclaredField("__prefix"); + __prefix.setAccessible(true); Map>> typesmap = new LinkedHashMap<>(); for (NodeServer node : app.servers) { if (!(node instanceof NodeHttpServer)) continue; @@ -52,7 +55,7 @@ public class ApiDocsService extends DefaultRestServlet implements Service { continue; } final Map servletmap = new LinkedHashMap<>(); - String prefix = _prefix(servlet); + String prefix = (String) __prefix.get(servlet); String[] urlregs = ws.value(); if (prefix != null && !prefix.isEmpty()) { for (int i = 0; i < urlregs.length; i++) { @@ -73,7 +76,7 @@ public class ApiDocsService extends DefaultRestServlet implements Service { if (Modifier.isAbstract(clz.getModifiers())) break; for (Method method : clz.getMethods()) { if (method.getParameterCount() != 2) continue; - WebMapping action = method.getAnnotation(WebMapping.class); + HttpMapping action = method.getAnnotation(HttpMapping.class); if (action == null) continue; if (!action.inherited() && selfClz != clz) continue; //忽略不被继承的方法 final Map mappingmap = new LinkedHashMap<>(); @@ -123,7 +126,7 @@ public class ApiDocsService extends DefaultRestServlet implements Service { typesmap.put(rtype.getName(), typemap); } mappingmap.put("results", results); - for (WebParam param : method.getAnnotationsByType(WebParam.class)) { + for (HttpParam param : method.getAnnotationsByType(HttpParam.class)) { final Map parammap = new LinkedHashMap<>(); final boolean isarray = param.type().isArray(); final Class ptype = isarray ? param.type().getComponentType() : param.type(); diff --git a/src/org/redkale/net/Response.java b/src/org/redkale/net/Response.java index bfd754735..d5f0c4b2a 100644 --- a/src/org/redkale/net/Response.java +++ b/src/org/redkale/net/Response.java @@ -158,10 +158,22 @@ public abstract class Response> { } } + /** + * 使用 public void recycleListener(BiConsumer recycleListener) 代替 + * + * @param recycleListener BiConsumer + * + * @deprecated + */ + @Deprecated public void setRecycleListener(BiConsumer> recycleListener) { this.recycleListener = recycleListener; } + public void recycleListener(BiConsumer> recycleListener) { + this.recycleListener = recycleListener; + } + public Object getOutput() { return output; } diff --git a/src/org/redkale/net/http/DefaultRestServlet.java b/src/org/redkale/net/http/DefaultRestServlet.java index 3c579698b..244e572fa 100644 --- a/src/org/redkale/net/http/DefaultRestServlet.java +++ b/src/org/redkale/net/http/DefaultRestServlet.java @@ -21,9 +21,4 @@ public class DefaultRestServlet extends RestHttpServlet { return new Object(); } - @Override - public void authenticate(int moduleid, int actionid, HttpRequest request, HttpResponse response, HttpServlet next) throws IOException { - next.execute(request, response); - } - } diff --git a/src/org/redkale/net/http/HttpBaseServlet.java b/src/org/redkale/net/http/HttpBaseServlet.java index 871631d48..8facdfbda 100644 --- a/src/org/redkale/net/http/HttpBaseServlet.java +++ b/src/org/redkale/net/http/HttpBaseServlet.java @@ -22,31 +22,31 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*; import org.redkale.service.RetResult; /** + * 直接只用HttpServlet代替, 将在1.9版本移除 * *

* 详情见: https://redkale.org * + * @deprecated * @author zhangjx */ +@Deprecated public abstract class HttpBaseServlet extends HttpServlet { - public static final int RET_SERVER_ERROR = 1800_0001; - - public static final int RET_METHOD_ERROR = 1800_0002; - /** - * 配合 HttpBaseServlet 使用。 - * 当标记为 @AuthIgnore 的方法在执行execute之前不会调用authenticate 方法。 + * 使用 org.redkale.util.AuthIgnore 代替 * *

* 详情见: https://redkale.org * + * @deprecated * @author zhangjx */ @Inherited @Documented @Target({METHOD, TYPE}) @Retention(RUNTIME) + @Deprecated protected @interface AuthIgnore { } @@ -66,18 +66,20 @@ public abstract class HttpBaseServlet extends HttpServlet { } /** - * 配合 @WebMapping 使用。 - * 用于对@WebMapping方法中参数描述 + * + * 使用 org.redkale.net.http.HttpParam 代替 * *

* 详情见: https://redkale.org * + * @deprecated * @author zhangjx */ @Documented @Target({METHOD}) @Retention(RUNTIME) @Repeatable(WebParams.class) + @Deprecated protected @interface WebParam { String name(); //参数名 @@ -102,16 +104,17 @@ public abstract class HttpBaseServlet extends HttpServlet { } /** - * 使用 WebMapping 替代。 + * 使用 org.redkale.net.http.HttpMapping 替代。 *

* 详情见: https://redkale.org * + * @deprecated * @author zhangjx */ - @Deprecated @Documented @Target({METHOD}) @Retention(RUNTIME) + @Deprecated protected @interface WebAction { int actionid() default 0; @@ -130,17 +133,18 @@ public abstract class HttpBaseServlet extends HttpServlet { } /** - * 配合 HttpBaseServlet 使用。 - * 用于对@WebServlet对应的url进行细分。 其url必须是包含WebServlet中定义的前缀, 且不能是正则表达式 + * 使用 org.redkale.net.http.HttpMapping 替代。 * *

* 详情见: https://redkale.org * + * @deprecated * @author zhangjx */ @Documented @Target({METHOD}) @Retention(RUNTIME) + @Deprecated protected @interface WebMapping { int actionid() default 0; @@ -159,18 +163,15 @@ public abstract class HttpBaseServlet extends HttpServlet { } /** - * 配合 HttpBaseServlet 使用。 - * 当标记为 @HttpCacheable 的方法使用response.finish的参数将被缓存一段时间(默认值 seconds=15秒)。 - * 通常情况下 @HttpCacheable 需要与 @AuthIgnore 一起使用,没有标记@AuthIgnore的方法一般输出的结果与当前用户信息有关。 - * - *

- * 详情见: https://redkale.org + * 使用 org.redkale.net.http.HttpCacheable 替代。 * + * @deprecated * @author zhangjx */ @Documented @Target({METHOD}) @Retention(RUNTIME) + @Deprecated protected @interface HttpCacheable { /** @@ -215,8 +216,7 @@ public abstract class HttpBaseServlet extends HttpServlet { if (entry.ignore) { authSuccessServlet.execute(request, response); } else { - response.nextEvent(authSuccessServlet); - authenticate(entry.moduleid, entry.actionid, request, response, authSuccessServlet); + HttpBaseServlet.this.authenticate(entry.moduleid, entry.actionid, request, response, authSuccessServlet); } return; } @@ -225,43 +225,29 @@ public abstract class HttpBaseServlet extends HttpServlet { } }; - /** - * 使用 public void preExecute(HttpRequest request, HttpResponse response) throws IOException 代替 - * - * @param request HttpRequest - * @param response HttpResponse - * @param next HttpServlet - * - * @deprecated - * - * @throws IOException IOException - */ - @Deprecated - public void preExecute(HttpRequest request, HttpResponse response, final HttpServlet next) throws IOException { - } - /** *

* 预执行方法,在execute方法之前运行,通常用于常规统计或基础检测,例如 :
*

      *      @Override
-     *      public void preExecute(final HttpRequest request, final HttpResponse response) throws IOException {
+     *      public void preExecute(final HttpRequest request, final HttpResponse response, HttpServlet next) throws IOException {
      *          if (finer) response.setRecycleListener((req, resp) -> {  //记录处理时间比较长的请求
      *              long e = System.currentTimeMillis() - ((HttpRequest) req).getCreatetime();
      *              if (e > 200) logger.finer("http-execute-cost-time: " + e + " ms. request = " + req);
      *          });
-     *          response.nextEvent();
+     *          next.execute(request, response);
      *      }
      * 
*

* * @param request HttpRequest * @param response HttpResponse + * @param next HttpServlet * * @throws IOException IOException */ - public void preExecute(HttpRequest request, HttpResponse response) throws IOException { - response.nextEvent(); + public void preExecute(HttpRequest request, HttpResponse response, final HttpServlet next) throws IOException { + next.execute(request, response); } /** @@ -313,10 +299,7 @@ public abstract class HttpBaseServlet extends HttpServlet { @Override public final void execute(HttpRequest request, HttpResponse response) throws IOException { - response.nextEvent(preSuccessServlet); - preExecute(request, response); - //兼容以前 - //preExecute(request, response, preSuccessServlet); + preExecute(request, response, preSuccessServlet); } public final void preInit(HttpContext context, AnyValue config) { diff --git a/src/org/redkale/net/http/HttpCacheable.java b/src/org/redkale/net/http/HttpCacheable.java new file mode 100644 index 000000000..5b36e98fb --- /dev/null +++ b/src/org/redkale/net/http/HttpCacheable.java @@ -0,0 +1,33 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 配合 HttpServlet 使用。 + * 当标记为 @HttpCacheable 的方法使用response.finish的参数将被缓存一段时间(默认值 seconds=15秒)。 + * 通常情况下 @HttpCacheable 需要与 @AuthIgnore 一起使用,没有标记@AuthIgnore的方法一般输出的结果与当前用户信息有关。 + * + *

+ * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Documented +@Target({METHOD}) +@Retention(RUNTIME) +public @interface HttpCacheable { + + /** + * 超时的秒数 + * + * @return 超时秒数 + */ + int seconds() default 15; +} diff --git a/src/org/redkale/net/http/HttpContext.java b/src/org/redkale/net/http/HttpContext.java index 3cd94165b..c436c8614 100644 --- a/src/org/redkale/net/http/HttpContext.java +++ b/src/org/redkale/net/http/HttpContext.java @@ -11,6 +11,8 @@ import java.nio.charset.*; import java.security.*; import java.util.concurrent.*; import java.util.logging.*; +import jdk.internal.org.objectweb.asm.*; +import static jdk.internal.org.objectweb.asm.Opcodes.*; import org.redkale.net.*; import org.redkale.util.*; import org.redkale.watch.*; @@ -27,6 +29,8 @@ public class HttpContext extends Context { protected final SecureRandom random = new SecureRandom(); + protected final ConcurrentHashMap asyncHandlerCreators = new ConcurrentHashMap<>(); + public HttpContext(long serverStartTime, Logger logger, ExecutorService executor, int bufferCapacity, ObjectPool bufferPool, ObjectPool responsePool, int maxbody, Charset charset, InetSocketAddress address, PrepareServlet prepare, WatchFactory watch, int readTimeoutSecond, int writeTimeoutSecond) { @@ -54,4 +58,113 @@ public class HttpContext extends Context { return responsePool; } + protected Creator loadAsyncHandlerCreator(Class handlerClass) { + Creator creator = asyncHandlerCreators.get(handlerClass); + if (creator == null) { + creator = createAsyncHandlerCreator(handlerClass); + asyncHandlerCreators.put(handlerClass, creator); + } + return creator; + } + + private Creator createAsyncHandlerCreator(Class handlerClass) { + //生成规则与SncpAsyncHandler.Factory 很类似 + //------------------------------------------------------------- + final boolean handlerinterface = handlerClass.isInterface(); + final String handlerClassName = handlerClass.getName().replace('.', '/'); + final String handlerName = AsyncHandler.class.getName().replace('.', '/'); + final String handlerDesc = Type.getDescriptor(AsyncHandler.class); + final String newDynName = handlerClass.getName().replace('.', '/') + "_Dync" + AsyncHandler.class.getSimpleName() + "_" + (System.currentTimeMillis() % 10000); + + ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); + FieldVisitor fv; + AsmMethodVisitor mv; + AnnotationVisitor av0; + cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, newDynName, null, handlerinterface ? "java/lang/Object" : handlerClassName, handlerinterface ? new String[]{handlerClassName} : new String[]{handlerName}); + + { //handler 属性 + fv = cw.visitField(ACC_PRIVATE, "handler", handlerDesc, null, null); + fv.visitEnd(); + } + {//构造方法 + mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "", "(" + handlerDesc + ")V", null, null)); + //mv.setDebug(true); + { + av0 = mv.visitAnnotation("Ljava/beans/ConstructorProperties;", true); + { + AnnotationVisitor av1 = av0.visitArray("value"); + av1.visit(null, "handler"); + av1.visitEnd(); + } + av0.visitEnd(); + } + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, handlerinterface ? "java/lang/Object" : handlerClassName, "", "()V", false); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitFieldInsn(PUTFIELD, newDynName, "handler", handlerDesc); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 2); + mv.visitEnd(); + } + + for (java.lang.reflect.Method method : handlerClass.getMethods()) { // + if ("completed".equals(method.getName()) && method.getParameterCount() == 2) { + mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "completed", Type.getMethodDescriptor(method), null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "handler", handlerDesc); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "completed", "(Ljava/lang/Object;Ljava/lang/Object;)V", true); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } else if ("failed".equals(method.getName()) && method.getParameterCount() == 2) { + mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, "failed", Type.getMethodDescriptor(method), null, null)); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, "handler", handlerDesc); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEINTERFACE, handlerName, "failed", "(Ljava/lang/Throwable;Ljava/lang/Object;)V", true); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } else if (handlerinterface || java.lang.reflect.Modifier.isAbstract(method.getModifiers())) { + mv = new AsmMethodVisitor(cw.visitMethod(ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(method), null, null)); + Class returnType = method.getReturnType(); + if (returnType == void.class) { + mv.visitInsn(RETURN); + mv.visitMaxs(0, 1); + } else if (returnType.isPrimitive()) { + mv.visitInsn(ICONST_0); + if (returnType == long.class) { + mv.visitInsn(LRETURN); + mv.visitMaxs(2, 1); + } else if (returnType == float.class) { + mv.visitInsn(FRETURN); + mv.visitMaxs(2, 1); + } else if (returnType == double.class) { + mv.visitInsn(DRETURN); + mv.visitMaxs(2, 1); + } else { + mv.visitInsn(IRETURN); + mv.visitMaxs(1, 1); + } + } else { + mv.visitInsn(ACONST_NULL); + mv.visitInsn(ARETURN); + mv.visitMaxs(1, 1); + } + mv.visitEnd(); + } + } + cw.visitEnd(); + byte[] bytes = cw.toByteArray(); + Class newHandlerClazz = (Class) new ClassLoader(handlerClass.getClassLoader()) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + return (Creator) Creator.create(newHandlerClazz); + } } diff --git a/src/org/redkale/net/http/HttpMapping.java b/src/org/redkale/net/http/HttpMapping.java new file mode 100644 index 000000000..6326de2f2 --- /dev/null +++ b/src/org/redkale/net/http/HttpMapping.java @@ -0,0 +1,39 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 配合 HttpServlet 使用。 + * 用于对@WebServlet对应的url进行细分。 其url必须是包含WebServlet中定义的前缀, 且不能是正则表达式 + * + *

+ * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Documented +@Target({METHOD}) +@Retention(RUNTIME) +public @interface HttpMapping { + + int actionid() default 0; + + String url(); + + String[] methods() default {};//允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法 + + String comment() default ""; //备注描述 + + boolean inherited() default true; //是否能被继承, 当 HttpServlet 被继承后该方法是否能被子类继承 + + String result() default "Object"; //输出结果的数据类型 + + Class[] results() default {}; //输出结果的数据类型集合,由于结果类型可能是泛型而注解的参数值不支持泛型,因此加入明细数据类型集合 +} diff --git a/src/org/redkale/net/http/HttpParam.java b/src/org/redkale/net/http/HttpParam.java new file mode 100644 index 000000000..77175f416 --- /dev/null +++ b/src/org/redkale/net/http/HttpParam.java @@ -0,0 +1,61 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 配合 @HttpMapping 使用。 + * 用于对@HttpMapping方法中参数描述 + * + *

+ * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Documented +@Target({METHOD}) +@Retention(RUNTIME) +@Repeatable(HttpParam.HttpParams.class) +public @interface HttpParam { + + String name(); //参数名 + + Class type(); //参数的数据类型 + + String comment() default ""; //备注描述 + + HttpParamSourceType src() default HttpParamSourceType.PARAMETER; //参数来源类型 + + int radix() default 10; //转换数字byte/short/int/long时所用的进制数, 默认10进制 + + boolean required() default true; //参数是否必传 + + /** + * 配合 @WebParam 使用。 + * 用于对@WebParam中参数的来源类型 + * + *

+ * 详情见: https://redkale.org + * + * @author zhangjx + */ + public static enum HttpParamSourceType { + + PARAMETER, HEADER, COOKIE; + } + + @Documented + @Target({METHOD}) + @Retention(RUNTIME) + @interface HttpParams { + + HttpParam[] value(); + } + +} diff --git a/src/org/redkale/net/http/HttpPrepareServlet.java b/src/org/redkale/net/http/HttpPrepareServlet.java index 04a3733dd..4c947beae 100644 --- a/src/org/redkale/net/http/HttpPrepareServlet.java +++ b/src/org/redkale/net/http/HttpPrepareServlet.java @@ -43,11 +43,7 @@ public class HttpPrepareServlet extends PrepareServlet servlets = getServlets(); servlets.forEach(s -> { - if (s instanceof WebSocketServlet) { - ((WebSocketServlet) s).preInit(context, getServletConf(s)); - } else if (s instanceof HttpBaseServlet) { - ((HttpBaseServlet) s).preInit(context, getServletConf(s)); - } + s.preInit(context, getServletConf(s)); s.init(context, getServletConf(s)); }); final WatchFactory watch = context.getWatchFactory(); @@ -213,11 +209,7 @@ public class HttpPrepareServlet extends PrepareServlet { s.destroy(context, getServletConf(s)); - if (s instanceof WebSocketServlet) { - ((WebSocketServlet) s).postDestroy(context, getServletConf(s)); - } else if (s instanceof HttpBaseServlet) { - ((HttpBaseServlet) s).postDestroy(context, getServletConf(s)); - } + s.postDestroy(context, getServletConf(s)); }); this.allMapStrings.clear(); this.wsmappings.clear(); diff --git a/src/org/redkale/net/http/HttpRequest.java b/src/org/redkale/net/http/HttpRequest.java index e71ff0ed6..fb26631e1 100644 --- a/src/org/redkale/net/http/HttpRequest.java +++ b/src/org/redkale/net/http/HttpRequest.java @@ -64,6 +64,10 @@ public class HttpRequest extends Request { protected boolean boundary = false; + protected int moduleid; + + protected int actionid; + private final String remoteAddrHeader; Object attachment; //供 HttpBaseServlet传递Entry使用 @@ -236,6 +240,24 @@ public class HttpRequest extends Request { return super.removeProperty(name); } + /** + * 获取模块ID,来自@HttpServlet.moduleid() + * + * @return 模块ID + */ + public int getModuleid() { + return this.moduleid; + } + + /** + * 获取操作ID,来自@HttpMapping.actionid() + * + * @return 模块ID + */ + public int getActionid() { + return this.actionid; + } + /** * 获取客户端地址IP * @@ -350,6 +372,8 @@ public class HttpRequest extends Request { this.contentLength = -1; this.boundary = false; this.bodyparsed = false; + this.moduleid = 0; + this.actionid = 0; this.attachment = null; diff --git a/src/org/redkale/net/http/HttpResponse.java b/src/org/redkale/net/http/HttpResponse.java index ed0675a3f..ae384318c 100644 --- a/src/org/redkale/net/http/HttpResponse.java +++ b/src/org/redkale/net/http/HttpResponse.java @@ -213,7 +213,7 @@ public class HttpResponse extends Response { } /** - * 创建AsyncHandler实例,将非字符串对象以JSON格式输出,字符串以文本输出 + * 创建AsyncHandler实例 * * @return AsyncHandler */ @@ -232,6 +232,21 @@ public class HttpResponse extends Response { }); } + /** + * 创建AsyncHandler子类的实例
+ * + * 传入的AsyncHandler子类必须是public,且保证其子类可被继承且completed、failed可被重载且包含空参数的构造函数。 + * + * @param 泛型 + * @param handlerClass AsyncHandler子类 + * + * @return AsyncHandler AsyncHandler + */ + public H createAsyncHandler(Class handlerClass) { + if (handlerClass == null || handlerClass == AsyncHandler.class) return (H) createAsyncHandler(); + return context.loadAsyncHandlerCreator(handlerClass).create(createAsyncHandler()); + } + /** * 将对象以JSON格式输出 * @@ -354,6 +369,44 @@ public class HttpResponse extends Response { }); } + /** + * 将HttpResult的结果对象以JSON格式输出 + * + * @param result HttpResult对象 + */ + public void finishJson(final HttpResult result) { + finishJson(request.getJsonConvert(), result); + } + + /** + * 将HttpResult的结果对象以JSON格式输出 + * + * @param convert 指定的JsonConvert + * @param result HttpResult对象 + */ + public void finishJson(final JsonConvert convert, final HttpResult result) { + if (output == null) { + finish(""); + return; + } + if (result.getContentType() != null) setContentType(result.getContentType()); + addHeader(result.getHeaders()).addCookie(result.getCookies()).setStatus(result.getStatus() < 1 ? 200 : result.getStatus()); + if (result.getResult() instanceof File) { + try { + finish((File) result.getResult()); + } catch (IOException e) { + getContext().getLogger().log(Level.WARNING, "HttpServlet finishJson HttpResult File occur, forece to close channel. request = " + getRequest(), e); + finish(500, null); + } + } else if (result.getResult() instanceof String) { + finish((String) result.getResult()); + } else if (result.getResult() == null) { + finish(result.getMessage()); + } else { + finishJson(result.getResult()); + } + } + /** * 将CompletableFuture的结果对象以JSON格式输出 * diff --git a/src/org/redkale/net/http/HttpResult.java b/src/org/redkale/net/http/HttpResult.java new file mode 100644 index 000000000..f0ec9e8e0 --- /dev/null +++ b/src/org/redkale/net/http/HttpResult.java @@ -0,0 +1,103 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.net.http; + +import java.io.Serializable; +import java.net.HttpCookie; +import java.util.*; + +/** + * + *

+ * 详情见: https://redkale.org + * + * @author zhangjx + * @param 结果对象的类型 + */ +public class HttpResult { + + public static final String SESSIONID_COOKIENAME = HttpRequest.SESSIONID_NAME; + + private Map headers; + + private List cookies; + + private String contentType; + + private T result; + + private int status = 0; //不设置则为 200 + + private String message; + + public HttpResult() { + } + + public HttpResult(T result) { + this.result = result; + } + + public HttpResult addHeader(String name, Serializable value) { + if (this.headers == null) this.headers = new HashMap<>(); + this.headers.put(name, String.valueOf(value)); + return this; + } + + public HttpResult addCookie(HttpCookie cookie) { + if (this.cookies == null) this.cookies = new ArrayList<>(); + this.cookies.add(cookie); + return this; + } + + public Map getHeaders() { + return headers; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public List getCookies() { + return cookies; + } + + public void setCookies(List cookies) { + this.cookies = cookies; + } + + public String getContentType() { + return contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + + public T getResult() { + return result; + } + + public void setResult(T result) { + this.result = result; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + +} diff --git a/src/org/redkale/net/http/HttpServlet.java b/src/org/redkale/net/http/HttpServlet.java index 2766285c8..f42e1713a 100644 --- a/src/org/redkale/net/http/HttpServlet.java +++ b/src/org/redkale/net/http/HttpServlet.java @@ -5,9 +5,20 @@ */ package org.redkale.net.http; -import org.redkale.net.Servlet; +import java.io.IOException; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import jdk.internal.org.objectweb.asm.*; +import static jdk.internal.org.objectweb.asm.ClassWriter.COMPUTE_FRAMES; +import static jdk.internal.org.objectweb.asm.Opcodes.*; +import org.redkale.net.*; +import org.redkale.service.RetResult; +import org.redkale.util.*; /** + * HTTP版的Servlet, 执行顺序 execute --> preExecute --> authenticate --> HttpMapping对应的方法 * *

* 详情见: https://redkale.org @@ -16,6 +27,337 @@ import org.redkale.net.Servlet; */ public abstract class HttpServlet extends Servlet { + public static final int RET_SERVER_ERROR = 1800_0001; + + public static final int RET_METHOD_ERROR = 1800_0002; + String _prefix = ""; //当前HttpServlet的path前缀 + private Map.Entry[] mappings; + + //这里不能直接使用HttpServlet,会造成死循环初始化HttpServlet + private final Servlet authSuccessServlet = new Servlet() { + @Override + public void execute(HttpRequest request, HttpResponse response) throws IOException { + Entry entry = (Entry) request.attachment; + if (entry.cacheseconds > 0) {//有缓存设置 + CacheEntry ce = entry.cache.get(request.getRequestURI()); + if (ce != null && ce.time + entry.cacheseconds > System.currentTimeMillis()) { //缓存有效 + response.setStatus(ce.status); + response.setContentType(ce.contentType); + response.finish(ce.getBuffers()); + return; + } + response.setBufferHandler(entry.cacheHandler); + } + entry.servlet.execute(request, response); + } + }; + + //preExecute运行完后执行的Servlet + private final Servlet preSuccessServlet = new Servlet() { + @Override + public void execute(HttpRequest request, HttpResponse response) throws IOException { + for (Map.Entry en : mappings) { + if (request.getRequestURI().startsWith(en.getKey())) { + Entry entry = en.getValue(); + if (!entry.checkMethod(request.getMethod())) { + response.finishJson(new RetResult(RET_METHOD_ERROR, "Method(" + request.getMethod() + ") Error")); + return; + } + request.attachment = entry; + request.moduleid = entry.moduleid; + request.actionid = entry.actionid; + if (entry.ignore) { + authSuccessServlet.execute(request, response); + } else { + response.nextEvent(authSuccessServlet); + authenticate(request, response); + } + return; + } + } + throw new IOException(this.getClass().getName() + " not found method for URI(" + request.getRequestURI() + ")"); + } + }; + + void preInit(HttpContext context, AnyValue config) { + String path = _prefix == null ? "" : _prefix; + WebServlet ws = this.getClass().getAnnotation(WebServlet.class); + if (ws != null && !ws.repair()) path = ""; + HashMap map = load(); + this.mappings = new Map.Entry[map.size()]; + int i = -1; + for (Map.Entry en : map.entrySet()) { + mappings[++i] = new AbstractMap.SimpleEntry<>(path + en.getKey(), en.getValue()); + } + //必须要倒排序, /query /query1 /query12 确保含子集的优先匹配 /query12 /query1 /query + Arrays.sort(mappings, (o1, o2) -> o2.getKey().compareTo(o1.getKey())); + } + + void postDestroy(HttpContext context, AnyValue config) { + } + + /** + *

+ * 预执行方法,在execute方法之前运行,通常用于常规统计或基础检测,例如 :
+ *

+     *      @Override
+     *      public void preExecute(final HttpRequest request, final HttpResponse response) throws IOException {
+     *          if (finer) response.recycleListener((req, resp) -> {  //记录处理时间比较长的请求
+     *              long e = System.currentTimeMillis() - ((HttpRequest) req).getCreatetime();
+     *              if (e > 200) logger.finer("http-execute-cost-time: " + e + " ms. request = " + req);
+     *          });
+     *          response.nextEvent();
+     *      }
+     * 
+ *

+ * + * @param request HttpRequest + * @param response HttpResponse + * + * @throws IOException IOException + */ + protected void preExecute(HttpRequest request, HttpResponse response) throws IOException { + response.nextEvent(); + } + + /** + *

+ * 用户登录或权限验证, 没有注解为@AuthIgnore 的方法会执行authenticate方法, 若验证成功则必须调用response.nextEvent();进行下一步操作, 例如:
+ *

+     *      @Override
+     *      public void authenticate(HttpRequest request, HttpResponse response) throws IOException {
+     *          UserInfo info = currentUser(request);
+     *          if (info == null) {
+     *              response.finishJson(RET_UNLOGIN);
+     *              return;
+     *          } else if (!info.checkAuth(request.getModuleid(), request.getActionid())) {
+     *              response.finishJson(RET_AUTHILLEGAL);
+     *              return;
+     *          }
+     *          response.nextEvent();
+     *      }
+     * 
+ *

+ * + * + * @param request HttpRequest + * @param response HttpResponse + * + * @throws IOException IOException + */ + protected void authenticate(HttpRequest request, HttpResponse response) throws IOException { + response.nextEvent(); + } + + @Override + public void execute(HttpRequest request, HttpResponse response) throws IOException { + response.nextEvent(preSuccessServlet); + preExecute(request, response); + } + + private HashMap load() { + final boolean typeIgnore = this.getClass().getAnnotation(AuthIgnore.class) != null; + WebServlet module = this.getClass().getAnnotation(WebServlet.class); + final int serviceid = module == null ? 0 : module.moduleid(); + final HashMap map = new HashMap<>(); + HashMap nameset = new HashMap<>(); + final Class selfClz = this.getClass(); + Class clz = this.getClass(); + do { + if (java.lang.reflect.Modifier.isAbstract(clz.getModifiers())) break; + for (final Method method : clz.getMethods()) { + //----------------------------------------------- + String methodname = method.getName(); + if ("service".equals(methodname) || "preExecute".equals(methodname) || "execute".equals(methodname) || "authenticate".equals(methodname)) continue; + //----------------------------------------------- + Class[] paramTypes = method.getParameterTypes(); + if (paramTypes.length != 2 || paramTypes[0] != HttpRequest.class + || paramTypes[1] != HttpResponse.class) continue; + //----------------------------------------------- + Class[] exps = method.getExceptionTypes(); + if (exps.length > 0 && (exps.length != 1 || exps[0] != IOException.class)) continue; + //----------------------------------------------- + + final HttpMapping mapping = method.getAnnotation(HttpMapping.class); + if (mapping == null) continue; + final boolean inherited = mapping.inherited(); + if (!inherited && selfClz != clz) continue; //忽略不被继承的方法 + final int actionid = mapping.actionid(); + final String name = mapping.url().trim(); + final String[] methods = mapping.methods(); + if (nameset.containsKey(name)) { + if (nameset.get(name) != clz) continue; + throw new RuntimeException(this.getClass().getSimpleName() + " have two same " + HttpMapping.class.getSimpleName() + "(" + name + ")"); + } + nameset.put(name, clz); + map.put(name, new Entry(typeIgnore, serviceid, actionid, name, methods, method, createHttpServlet(method))); + } + } while ((clz = clz.getSuperclass()) != HttpServlet.class); + return map; + } + + private HttpServlet createHttpServlet(final Method method) { + //------------------------------------------------------------------------------ + final String supDynName = HttpServlet.class.getName().replace('.', '/'); + final String interName = this.getClass().getName().replace('.', '/'); + final String interDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(this.getClass()); + final String requestSupDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(Request.class); + final String responseSupDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(Response.class); + final String requestDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(HttpRequest.class); + final String responseDesc = jdk.internal.org.objectweb.asm.Type.getDescriptor(HttpResponse.class); + String newDynName = interName + "_Dyn_" + method.getName(); + int i = 0; + for (;;) { + try { + Class.forName(newDynName.replace('/', '.')); + newDynName += "_" + (++i); + } catch (Throwable ex) { + break; + } + } + //------------------------------------------------------------------------------ + ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); + FieldVisitor fv; + MethodVisitor mv; + AnnotationVisitor av0; + final String factfield = "_factServlet"; + cw.visit(V1_8, ACC_PUBLIC + ACC_FINAL + ACC_SUPER, newDynName, null, supDynName, null); + { + fv = cw.visitField(ACC_PUBLIC, factfield, interDesc, null, null); + fv.visitEnd(); + } + { //构造函数 + mv = (cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)); + //mv.setDebug(true); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, supDynName, "", "()V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + { + mv = (cw.visitMethod(ACC_PUBLIC, "execute", "(" + requestDesc + responseDesc + ")V", null, new String[]{"java/io/IOException"})); + mv.visitVarInsn(ALOAD, 0); + mv.visitFieldInsn(GETFIELD, newDynName, factfield, interDesc); + mv.visitVarInsn(ALOAD, 1); + mv.visitVarInsn(ALOAD, 2); + mv.visitMethodInsn(INVOKEVIRTUAL, interName, method.getName(), "(" + requestDesc + responseDesc + ")V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC + ACC_BRIDGE + ACC_SYNTHETIC, "execute", "(" + requestSupDesc + responseSupDesc + ")V", null, new String[]{"java/io/IOException"}); + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, 1); + mv.visitTypeInsn(CHECKCAST, HttpRequest.class.getName().replace('.', '/')); + mv.visitVarInsn(ALOAD, 2); + mv.visitTypeInsn(CHECKCAST, HttpResponse.class.getName().replace('.', '/')); + mv.visitMethodInsn(INVOKEVIRTUAL, newDynName, "execute", "(" + requestDesc + responseDesc + ")V", false); + mv.visitInsn(RETURN); + mv.visitMaxs(3, 3); + mv.visitEnd(); + } + cw.visitEnd(); + //------------------------------------------------------------------------------ + byte[] bytes = cw.toByteArray(); + Class newClazz = new ClassLoader(this.getClass().getClassLoader()) { + public final Class loadClass(String name, byte[] b) { + return defineClass(name, b, 0, b.length); + } + }.loadClass(newDynName.replace('/', '.'), bytes); + try { + HttpServlet instance = (HttpServlet) newClazz.newInstance(); + instance.getClass().getField(factfield).set(instance, this); + return instance; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + + private static final class Entry { + + public Entry(boolean typeIgnore, int moduleid, int actionid, String name, String[] methods, Method method, HttpServlet servlet) { + this.moduleid = moduleid; + this.actionid = actionid; + this.name = name; + this.methods = methods; + this.method = method; + this.servlet = servlet; + this.ignore = typeIgnore || method.getAnnotation(AuthIgnore.class) != null; + HttpCacheable hc = method.getAnnotation(HttpCacheable.class); + this.cacheseconds = hc == null ? 0 : hc.seconds() * 1000; + this.cache = cacheseconds > 0 ? new ConcurrentHashMap() : null; + this.cacheHandler = cacheseconds > 0 ? (HttpResponse response, ByteBuffer[] buffers) -> { + int status = response.getStatus(); + if (status != 200) return null; + CacheEntry ce = new CacheEntry(response.getStatus(), response.getContentType(), buffers); + cache.put(response.getRequest().getRequestURI(), ce); + return ce.getBuffers(); + } : null; + } + + public boolean isNeedCheck() { + return this.moduleid != 0 || this.actionid != 0; + } + + public boolean checkMethod(final String reqMethod) { + if (methods.length == 0) return true; + for (String m : methods) { + if (reqMethod.equalsIgnoreCase(m)) return true; + } + return false; + } + + public final HttpResponse.BufferHandler cacheHandler; + + public final ConcurrentHashMap cache; + + public final int cacheseconds; + + public final boolean ignore; + + public final int moduleid; + + public final int actionid; + + public final String name; + + public final String[] methods; + + public final Method method; + + public final HttpServlet servlet; + } + + private static final class CacheEntry { + + public final long time = System.currentTimeMillis(); + + private final ByteBuffer[] buffers; + + private final int status; + + private final String contentType; + + public CacheEntry(int status, String contentType, ByteBuffer[] bufs) { + this.status = status; + this.contentType = contentType; + final ByteBuffer[] newBuffers = new ByteBuffer[bufs.length]; + for (int i = 0; i < newBuffers.length; i++) { + newBuffers[i] = bufs[i].duplicate().asReadOnlyBuffer(); + } + this.buffers = newBuffers; + } + + public ByteBuffer[] getBuffers() { + final ByteBuffer[] newBuffers = new ByteBuffer[buffers.length]; + for (int i = 0; i < newBuffers.length; i++) { + newBuffers[i] = buffers[i].duplicate(); + } + return newBuffers; + } + } } diff --git a/src/org/redkale/net/http/Rest.java b/src/org/redkale/net/http/Rest.java index 9c44399c1..389d646f6 100644 --- a/src/org/redkale/net/http/Rest.java +++ b/src/org/redkale/net/http/Rest.java @@ -122,12 +122,12 @@ public final class Rest { final String flipperDesc = Type.getDescriptor(Flipper.class); final String restoutputDesc = Type.getDescriptor(RestOutput.class); final String attrDesc = Type.getDescriptor(org.redkale.util.Attribute.class); - final String authDesc = Type.getDescriptor(HttpBaseServlet.AuthIgnore.class); - final String cacheDesc = Type.getDescriptor(HttpBaseServlet.HttpCacheable.class); - final String mappingDesc = Type.getDescriptor(HttpBaseServlet.WebMapping.class); - final String webparamDesc = Type.getDescriptor(HttpBaseServlet.WebParam.class); - final String webparamsDesc = Type.getDescriptor(HttpBaseServlet.WebParams.class); - final String sourcetypeDesc = Type.getDescriptor(HttpBaseServlet.ParamSourceType.class); + final String authDesc = Type.getDescriptor(AuthIgnore.class); + final String cacheDesc = Type.getDescriptor(HttpCacheable.class); + final String mappingDesc = Type.getDescriptor(HttpMapping.class); + final String webparamDesc = Type.getDescriptor(HttpParam.class); + final String webparamsDesc = Type.getDescriptor(HttpParam.HttpParams.class); + final String sourcetypeDesc = Type.getDescriptor(HttpParam.HttpParamSourceType.class); final String reqInternalName = Type.getInternalName(HttpRequest.class); final String respInternalName = Type.getInternalName(HttpResponse.class); @@ -218,7 +218,7 @@ public final class Rest { final List entrys = new ArrayList<>(); final Map restAttributes = new LinkedHashMap<>(); - //获取所有可以转换成WebMapping的方法 + //获取所有可以转换成HttpMapping的方法 int methodidex = 0; final List paramtypes = new ArrayList<>(); for (final Method method : serviceType.getMethods()) { @@ -254,9 +254,9 @@ public final class Rest { } methodidex++; } - if (entrys.isEmpty()) return null; //没有可WebMapping的方法 + if (entrys.isEmpty()) return null; //没有可HttpMapping的方法 - //将每个Service可转换的方法生成HttpServlet对应的WebMapping方法 + //将每个Service可转换的方法生成HttpServlet对应的HttpMapping方法 final Map> asmParamMap = MethodParamClassVisitor.getMethodParamNames(serviceType); for (final MappingEntry entry : entrys) { final Method method = entry.mappingMethod; @@ -380,7 +380,7 @@ public final class Rest { Map mappingMap = new LinkedHashMap<>(); { // 设置 Annotation - //设置 WebMapping + //设置 HttpMapping boolean reqpath = false; for (Object[] ps : paramlist) { if ("#".equals((String) ps[1])) { @@ -425,8 +425,8 @@ public final class Rest { av2.visit("name", (String) ps[1]); av2.visit("type", Type.getType(Type.getDescriptor((Class) ps[2]))); av2.visit("radix", (Integer) ps[3]); - av2.visitEnum("src", sourcetypeDesc, ishead ? HttpBaseServlet.ParamSourceType.HEADER.name() - : (iscookie ? HttpBaseServlet.ParamSourceType.COOKIE.name() : HttpBaseServlet.ParamSourceType.PARAMETER.name())); + av2.visitEnum("src", sourcetypeDesc, ishead ? HttpParam.HttpParamSourceType.HEADER.name() + : (iscookie ? HttpParam.HttpParamSourceType.COOKIE.name() : HttpParam.HttpParamSourceType.PARAMETER.name())); av2.visit("comment", (String) ps[4]); av2.visit("required", (Boolean) ps[5]); av2.visitEnd(); diff --git a/src/org/redkale/net/http/RestHttpServlet.java b/src/org/redkale/net/http/RestHttpServlet.java index 5bb239b54..f501fb204 100644 --- a/src/org/redkale/net/http/RestHttpServlet.java +++ b/src/org/redkale/net/http/RestHttpServlet.java @@ -19,7 +19,7 @@ import org.redkale.util.*; * @author zhangjx * @param 当前用户对象类型 */ -public abstract class RestHttpServlet extends HttpBaseServlet { +public abstract class RestHttpServlet extends HttpServlet { protected abstract T currentUser(HttpRequest req) throws IOException; diff --git a/src/org/redkale/net/http/RestMapping.java b/src/org/redkale/net/http/RestMapping.java index daaec57d3..89236e9a3 100644 --- a/src/org/redkale/net/http/RestMapping.java +++ b/src/org/redkale/net/http/RestMapping.java @@ -40,7 +40,7 @@ public @interface RestMapping { String name() default ""; /** - * 备注描述, 对应@WebMapping.comment + * 备注描述, 对应@HttpMapping.comment * * @return String */ @@ -54,7 +54,7 @@ public @interface RestMapping { boolean auth() default false; /** - * 操作ID值,鉴权时用到, 对应@WebMapping.actionid + * 操作ID值,鉴权时用到, 对应@HttpMapping.actionid * * @return int */ @@ -68,7 +68,7 @@ public @interface RestMapping { int cacheseconds() default 0; /** - * 允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法, 对应@WebMapping.methods + * 允许方法(不区分大小写),如:GET/POST/PUT,为空表示允许所有方法, 对应@HttpMapping.methods * * @return String[] */ diff --git a/src/org/redkale/net/http/RestOutput.java b/src/org/redkale/net/http/RestOutput.java index 8855c8c40..b78e77f7b 100644 --- a/src/org/redkale/net/http/RestOutput.java +++ b/src/org/redkale/net/http/RestOutput.java @@ -10,13 +10,16 @@ import java.net.HttpCookie; import java.util.*; /** + * 使用 HttpResult 代替 * *

* 详情见: https://redkale.org * + * @deprecated * @author zhangjx * @param 结果对象的类型 */ +@Deprecated public class RestOutput { private Map headers; diff --git a/src/org/redkale/net/http/WebSocketServlet.java b/src/org/redkale/net/http/WebSocketServlet.java index 19dfc0eef..30c9eb7de 100644 --- a/src/org/redkale/net/http/WebSocketServlet.java +++ b/src/org/redkale/net/http/WebSocketServlet.java @@ -71,7 +71,8 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl protected WebSocketEngine engine; - public final void preInit(HttpContext context, AnyValue conf) { + @Override + final void preInit(HttpContext context, AnyValue conf) { InetSocketAddress addr = context.getServerAddress(); this.engine = new WebSocketEngine(addr.getHostString() + ":" + addr.getPort() + "-[" + resourceName() + "]", this.node, logger); if (this.node == null) this.node = createWebSocketNode(); @@ -84,7 +85,8 @@ public abstract class WebSocketServlet extends HttpServlet implements Resourcabl this.engine.init(conf); } - public final void postDestroy(HttpContext context, AnyValue conf) { + @Override + final void postDestroy(HttpContext context, AnyValue conf) { this.node.postDestroy(conf); super.destroy(context, conf); engine.close(); diff --git a/src/org/redkale/util/AuthIgnore.java b/src/org/redkale/util/AuthIgnore.java new file mode 100644 index 000000000..7cf214af6 --- /dev/null +++ b/src/org/redkale/util/AuthIgnore.java @@ -0,0 +1,27 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.redkale.util; + +import java.lang.annotation.*; +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * 配合 HttpServlet 使用。 + * 当标记为 @AuthIgnore 的方法在执行execute之前不会调用authenticate 方法。 + * + *

+ * 详情见: https://redkale.org + * + * @author zhangjx + */ +@Inherited +@Documented +@Target({METHOD, TYPE}) +@Retention(RUNTIME) +public @interface AuthIgnore { + +} diff --git a/test/org/redkale/test/rest/SimpleRestServlet.java b/test/org/redkale/test/rest/SimpleRestServlet.java index c21149a72..ad783ddc2 100644 --- a/test/org/redkale/test/rest/SimpleRestServlet.java +++ b/test/org/redkale/test/rest/SimpleRestServlet.java @@ -25,16 +25,16 @@ public class SimpleRestServlet extends RestHttpServlet { //普通鉴权 @Override - public void authenticate(int module, int actionid, HttpRequest request, HttpResponse response, HttpServlet next) throws IOException { + public void authenticate(HttpRequest request, HttpResponse response) throws IOException { UserInfo info = currentUser(request); if (info == null) { response.finishJson(RET_UNLOGIN); return; - } else if (!info.checkAuth(module, actionid)) { + } else if (!info.checkAuth(request.getModuleid(), request.getActionid())) { response.finishJson(RET_AUTHILLEGAL); return; } - next.execute(request, response); + response.nextEvent(); } } diff --git a/test/org/redkale/test/rest/_DynHelloRestServlet1.java b/test/org/redkale/test/rest/_DynHelloRestServlet1.java index 3cb4be660..64bce0019 100644 --- a/test/org/redkale/test/rest/_DynHelloRestServlet1.java +++ b/test/org/redkale/test/rest/_DynHelloRestServlet1.java @@ -48,7 +48,7 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet { } @AuthIgnore - @WebMapping(url = "/hello/create") + @HttpMapping(url = "/hello/create") public void create(HttpRequest req, HttpResponse resp) throws IOException { HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); @@ -60,7 +60,7 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet { } @AuthIgnore - @WebMapping(url = "/hello/delete/") + @HttpMapping(url = "/hello/delete/") public void delete(HttpRequest req, HttpResponse resp) throws IOException { HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); int id = Integer.parseInt(req.getRequstURILastPath()); @@ -69,7 +69,7 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet { } @AuthIgnore - @WebMapping(url = "/hello/update") + @HttpMapping(url = "/hello/update") public void update(HttpRequest req, HttpResponse resp) throws IOException { HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); String clientaddr = req.getRemoteAddr(); @@ -81,7 +81,7 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet { } @AuthIgnore - @WebMapping(url = "/hello/partupdate") + @HttpMapping(url = "/hello/partupdate") public void partupdate(HttpRequest req, HttpResponse resp) throws IOException { HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); @@ -93,7 +93,7 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet { } @AuthIgnore - @WebMapping(url = "/hello/query") + @HttpMapping(url = "/hello/query") public void query(HttpRequest req, HttpResponse resp) throws IOException { HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); HelloBean bean = req.getJsonParameter(HelloBean.class, "bean"); @@ -107,7 +107,7 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet { } @AuthIgnore - @WebMapping(url = "/hello/list") + @HttpMapping(url = "/hello/list") public void list(HttpRequest req, HttpResponse resp) throws IOException { HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); HelloBean bean = req.getJsonParameter(HelloBean.class, "bean"); @@ -120,7 +120,7 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet { } @AuthIgnore - @WebMapping(url = "/hello/find/") + @HttpMapping(url = "/hello/find/") public void find(HttpRequest req, HttpResponse resp) throws IOException { HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); int id = Integer.parseInt(req.getRequstURILastPath()); @@ -129,7 +129,7 @@ public class _DynHelloRestServlet1 extends SimpleRestServlet { } @AuthIgnore - @WebMapping(url = "/hello/asyncfind/") + @HttpMapping(url = "/hello/asyncfind/") public void asyncfind(HttpRequest req, HttpResponse resp) throws IOException { HelloService service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); int id = Integer.parseInt(req.getRequstURILastPath()); diff --git a/test/org/redkale/test/rest/_DynHelloRestServlet2.java b/test/org/redkale/test/rest/_DynHelloRestServlet2.java index 4e97eed6c..81dbaef34 100644 --- a/test/org/redkale/test/rest/_DynHelloRestServlet2.java +++ b/test/org/redkale/test/rest/_DynHelloRestServlet2.java @@ -11,7 +11,7 @@ import javax.annotation.Resource; import org.redkale.net.http.*; import org.redkale.service.RetResult; import org.redkale.source.Flipper; -import org.redkale.util.Sheet; +import org.redkale.util.*; /** * @@ -27,8 +27,8 @@ public class _DynHelloRestServlet2 extends SimpleRestServlet { private Map _servicemap; @AuthIgnore - @WebMapping(url = "/hello/create", comment = "创建Hello对象") - @WebParam(name = "bean", type = HelloEntity.class, comment = "Hello对象") + @HttpMapping(url = "/hello/create", comment = "创建Hello对象") + @HttpParam(name = "bean", type = HelloEntity.class, comment = "Hello对象") public void create(HttpRequest req, HttpResponse resp) throws IOException { HelloService2 service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); @@ -42,8 +42,8 @@ public class _DynHelloRestServlet2 extends SimpleRestServlet { } @AuthIgnore - @WebMapping(url = "/hello/delete/", comment = "根据id删除Hello对象") - @WebParam(name = "#", type = int.class, comment = "Hello对象id") + @HttpMapping(url = "/hello/delete/", comment = "根据id删除Hello对象") + @HttpParam(name = "#", type = int.class, comment = "Hello对象id") public void delete(HttpRequest req, HttpResponse resp) throws IOException { HelloService2 service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); int id = Integer.parseInt(req.getRequstURILastPath()); @@ -52,8 +52,8 @@ public class _DynHelloRestServlet2 extends SimpleRestServlet { } @AuthIgnore - @WebMapping(url = "/hello/update", comment = "修改Hello对象") - @WebParam(name = "bean", type = HelloEntity.class, comment = "Hello对象") + @HttpMapping(url = "/hello/update", comment = "修改Hello对象") + @HttpParam(name = "bean", type = HelloEntity.class, comment = "Hello对象") public void update(HttpRequest req, HttpResponse resp) throws IOException { HelloService2 service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); HelloEntity bean = req.getJsonParameter(HelloEntity.class, "bean"); @@ -66,8 +66,8 @@ public class _DynHelloRestServlet2 extends SimpleRestServlet { } @AuthIgnore - @WebMapping(url = "/hello/query", comment = "查询Hello对象列表") - @WebParam(name = "bean", type = HelloBean.class, comment = "过滤条件") + @HttpMapping(url = "/hello/query", comment = "查询Hello对象列表") + @HttpParam(name = "bean", type = HelloBean.class, comment = "过滤条件") public void query(HttpRequest req, HttpResponse resp) throws IOException { HelloService2 service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); HelloBean bean = req.getJsonParameter(HelloBean.class, "bean"); @@ -81,8 +81,8 @@ public class _DynHelloRestServlet2 extends SimpleRestServlet { } @AuthIgnore - @WebMapping(url = "/hello/find/", comment = "根据id删除Hello对象") - @WebParam(name = "#", type = int.class, comment = "Hello对象id") + @HttpMapping(url = "/hello/find/", comment = "根据id删除Hello对象") + @HttpParam(name = "#", type = int.class, comment = "Hello对象id") public void find(HttpRequest req, HttpResponse resp) throws IOException { HelloService2 service = _servicemap == null ? _service : _servicemap.get(req.getHeader(Rest.REST_HEADER_RESOURCE_NAME, "")); int id = Integer.parseInt(req.getRequstURILastPath());