|
29 | 29 | import com.yeetor.adb.AdbUtils; |
30 | 30 | import com.yeetor.minicap.Minicap; |
31 | 31 | import com.yeetor.util.Constant; |
32 | | -import eu.medsea.mimeutil.MimeUtil; |
| 32 | +import io.netty.buffer.ByteBuf; |
| 33 | +import io.netty.buffer.Unpooled; |
| 34 | +import io.netty.channel.ChannelFuture; |
33 | 35 | import io.netty.channel.ChannelFutureListener; |
34 | 36 | import io.netty.channel.ChannelHandlerContext; |
35 | 37 | import io.netty.handler.codec.http.*; |
|
38 | 40 |
|
39 | 41 | import javax.activation.MimetypesFileTypeMap; |
40 | 42 | import java.io.*; |
| 43 | +import java.lang.annotation.Annotation; |
| 44 | +import java.lang.reflect.InvocationTargetException; |
| 45 | +import java.lang.reflect.Method; |
41 | 46 |
|
42 | 47 | import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE; |
43 | 48 | import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION; |
|
46 | 51 |
|
47 | 52 | public class HttpServer { |
48 | 53 | private static Logger logger = Logger.getLogger(HttpServer.class); |
49 | | - public HttpResponse onRequest(ChannelHandlerContext ctx, HttpRequest request) { |
50 | | - String uri = request.uri(); |
51 | | - logger.info("http:" + uri); |
| 54 | + private static String INDEX_FILE = "index.html"; |
| 55 | + |
| 56 | + public HttpServer() { |
52 | 57 |
|
53 | | - if (uri.startsWith("/devices")) { |
54 | | - return devices(ctx, request); |
55 | | - } else if(uri.startsWith("/shot")) { |
56 | | - return shot(ctx, request); |
57 | | - } else { |
58 | | - return files(ctx, request); |
59 | | - } |
60 | 58 | } |
61 | 59 |
|
62 | | - public HttpResponse shot(ChannelHandlerContext ctx, HttpRequest request) { |
63 | | - |
| 60 | + public void onRequest(ChannelHandlerContext ctx, HttpRequest request, HttpResponse response) { |
64 | 61 | String uri = request.uri(); |
65 | | - String[] args = uri.split("/"); |
| 62 | + logger.info("http:" + uri); |
66 | 63 |
|
67 | | - if (args.length < 1 || args[1].length() == 0) { |
68 | | - return new DefaultFullHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.INTERNAL_SERVER_ERROR); |
| 64 | + // 找到符合注解的方法 |
| 65 | + Method[] methods = this.getClass().getDeclaredMethods(); |
| 66 | + for (Method method : methods) { |
| 67 | + Annotation[] annotations = method.getDeclaredAnnotations(); |
| 68 | + for (Annotation annotation : annotations) { |
| 69 | + if (annotation instanceof HttpRouter) { |
| 70 | + String routeUri = ((HttpRouter) annotation).uri(); |
| 71 | + if (uri.startsWith(routeUri)) { |
| 72 | + try { |
| 73 | + method.invoke(this, ctx, request, response); |
| 74 | + return; |
| 75 | + } catch (Exception e) { |
| 76 | + break; |
| 77 | + } |
| 78 | + } |
| 79 | + break; |
| 80 | + } |
| 81 | + } |
69 | 82 | } |
70 | 83 |
|
71 | | - String serialNumber = args[2]; |
72 | | - |
73 | | - long startTime=System.currentTimeMillis(); |
74 | | - Minicap cap = new Minicap(serialNumber); |
75 | | - byte[] data = cap.takeScreenShot(); |
76 | | - long endTime=System.currentTimeMillis(); |
77 | | - logger.info("ScreenShot used:" + (endTime - startTime) + "ms"); |
78 | | - |
79 | | - |
80 | | - return doDefaultResponse(ctx, request, data, "image/jpeg"); |
| 84 | + doFileRequest(ctx, request, response); |
81 | 85 | } |
82 | 86 |
|
83 | | - public HttpResponse devices(ChannelHandlerContext ctx, HttpRequest request) { |
84 | | - String json = AdbUtils.devices2JSON(); |
85 | | - byte[] data = json.getBytes(); |
86 | | - return doDefaultResponse(ctx, request, data, "text/json"); |
87 | | - } |
88 | | - |
89 | | - public HttpResponse files(ChannelHandlerContext ctx, HttpRequest request){ |
| 87 | + public void doFileRequest(ChannelHandlerContext ctx, HttpRequest request, HttpResponse response) { |
90 | 88 | String location = request.uri().substring(1); |
91 | 89 | if (location.equals("")) { |
92 | | - location = "index.html"; |
| 90 | + location = INDEX_FILE; |
93 | 91 | } |
94 | | - |
| 92 | + |
95 | 93 | File localFile = new File(Constant.getResourceDir(), "web" + File.separator + location); |
96 | | - |
| 94 | + |
97 | 95 | if (!localFile.exists() || localFile.isHidden()) { |
98 | | - return doDefaultResponse(ctx, request, "404".getBytes(), "text/plain"); |
| 96 | + writeErrorHttpResponse(ctx, request, response, HttpResponseStatus.NOT_FOUND); |
| 97 | + return; |
99 | 98 | } |
100 | | - |
101 | | - ChunkedFile chunkedFile = null; |
| 99 | + |
102 | 100 | try{ |
103 | 101 | RandomAccessFile raf = new RandomAccessFile(localFile, "r"); |
104 | | - chunkedFile = new ChunkedFile(raf, 0, localFile.length(), 8192); |
| 102 | + ChunkedFile chunkedFile = new ChunkedFile(raf, 0, localFile.length(), 8192); |
| 103 | + long fileSize = chunkedFile.length(); |
| 104 | + |
| 105 | + response.headers().add(CONTENT_LENGTH, localFile.length()); |
| 106 | + |
| 107 | + MimetypesFileTypeMap mimetypesFileTypeMap = new MimetypesFileTypeMap(); |
| 108 | + response.headers().add(CONTENT_TYPE, mimetypesFileTypeMap.getContentType(localFile.getPath())); |
| 109 | + |
| 110 | + |
| 111 | + writeResponse(ctx, request, response, chunkedFile); |
| 112 | + |
105 | 113 | }catch (IOException e) { |
106 | | - e.printStackTrace(); |
| 114 | + writeErrorHttpResponse(ctx, request, response, HttpResponseStatus.INTERNAL_SERVER_ERROR); |
| 115 | + return; |
107 | 116 | } |
108 | | - |
109 | | - |
110 | | - long l = chunkedFile.length(); |
111 | | - HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); |
112 | | - response.headers().add("Access-Control-Allow-Origin", "*"); |
113 | | - response.headers().add("Server", "Yeetor"); |
| 117 | + } |
114 | 118 |
|
115 | | - response.headers().add(CONTENT_LENGTH, localFile.length()); |
| 119 | + public HttpResponse doDefaultResponse(ChannelHandlerContext ctx, HttpRequest request, byte[] data, String contentType) { |
| 120 | + DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); |
| 121 | + response.headers().add("Content-Type", contentType); |
| 122 | + response.content().writeBytes(data); |
| 123 | + response.headers().add("Content-Length", data.length); |
| 124 | + return response; |
| 125 | + } |
116 | 126 |
|
| 127 | + @HttpRouter(uri="/devices") |
| 128 | + public void devices(ChannelHandlerContext ctx, HttpRequest request, HttpResponse response) { |
| 129 | + String json = AdbUtils.devices2JSON(); |
| 130 | + response.headers().set(CONTENT_TYPE, "text/plain"); |
| 131 | + writeHttpResponseWithString(ctx, request, response, json); |
| 132 | + } |
| 133 | + |
| 134 | + @HttpRouter(uri="/shot") |
| 135 | + public void shot(ChannelHandlerContext ctx, HttpRequest request, HttpResponse response) { |
| 136 | + String uri = request.uri(); |
| 137 | + String[] args = uri.split("/"); |
| 138 | + if (args.length < 1 || args[1].length() == 0) { |
| 139 | + writeErrorHttpResponse(ctx, request, response, HttpResponseStatus.NOT_FOUND); |
| 140 | + return; |
| 141 | + } |
117 | 142 |
|
118 | | - MimetypesFileTypeMap mimetypesFileTypeMap = new MimetypesFileTypeMap(); |
119 | | - response.headers().add(CONTENT_TYPE, mimetypesFileTypeMap.getContentType(localFile.getPath())); |
120 | | - |
| 143 | + String serialNumber = args[2]; |
| 144 | + long startTime=System.currentTimeMillis(); |
| 145 | + Minicap cap = new Minicap(serialNumber); |
| 146 | + byte[] data = cap.takeScreenShot(); |
| 147 | + long endTime=System.currentTimeMillis(); |
| 148 | + logger.info("ScreenShot used:" + (endTime - startTime) + "ms"); |
| 149 | + response.headers().set(CONTENT_TYPE, "image/jpeg"); |
| 150 | + HttpContent content = new DefaultHttpContent(Unpooled.wrappedBuffer(data)); |
| 151 | + response.headers().set(CONTENT_LENGTH, content.content().readableBytes()); |
| 152 | + writeResponse(ctx, request, response, content); |
| 153 | + } |
| 154 | + |
| 155 | + /** |
| 156 | + * 发送Response, |
| 157 | + * @param ctx |
| 158 | + * @param response |
| 159 | + */ |
| 160 | + public void writeHttpResponseWithString(ChannelHandlerContext ctx, HttpRequest request, HttpResponse response, String dataString) { |
| 161 | + HttpContent content = new DefaultHttpContent(Unpooled.wrappedBuffer(dataString.getBytes())); |
| 162 | + response.headers().set(CONTENT_LENGTH, content.content().readableBytes()); |
121 | 163 | ctx.write(response); |
122 | | - ctx.write(chunkedFile); |
123 | | - ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); // need? |
124 | | - |
125 | | - return response; |
| 164 | + ctx.write(content); |
| 165 | + ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); |
| 166 | + } |
| 167 | + |
| 168 | + public void writeErrorHttpResponse(ChannelHandlerContext ctx, HttpRequest request, HttpResponse response, HttpResponseStatus status) { |
| 169 | + response.setStatus(status); |
| 170 | + response.headers().set(CONTENT_LENGTH, 0); |
| 171 | + writeResponse(ctx, request, response); |
126 | 172 | } |
127 | 173 |
|
128 | | - public void doCommonResponse(ChannelHandlerContext ctx, HttpRequest request, HttpResponseStatus status) { |
129 | | - DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status); |
130 | | - response.headers().add("Content-Type", "text/json"); |
131 | | - response.headers().add("Server", "Yeetor"); |
| 174 | + public void writeResponse(ChannelHandlerContext ctx, HttpRequest request, HttpResponse response, Object... contents) { |
132 | 175 |
|
133 | | - String json = ""; |
134 | | - byte[] data = json.getBytes(); |
| 176 | + if (HttpUtil.isKeepAlive(request)) { |
| 177 | + response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE); |
| 178 | + } |
135 | 179 |
|
136 | | - response.content().writeBytes(data); |
137 | | - response.headers().add("Content-Length", data.length); |
138 | | - boolean isKeepAlive = HttpUtil.isKeepAlive(request); |
| 180 | + ctx.write(response); |
| 181 | + for (Object content : contents) { |
| 182 | + ctx.write(content); |
| 183 | + } |
| 184 | + ChannelFuture future = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT); |
139 | 185 |
|
140 | | - if (!isKeepAlive || response.status() != HttpResponseStatus.OK) { |
141 | | - ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); |
142 | | - } else { |
143 | | - response.headers().set(CONNECTION, KEEP_ALIVE); |
144 | | - ctx.writeAndFlush(response); |
| 186 | + if (!HttpUtil.isKeepAlive(request)) { |
| 187 | + future.addListener(ChannelFutureListener.CLOSE); |
145 | 188 | } |
146 | 189 | } |
147 | 190 |
|
148 | | - public HttpResponse doDefaultResponse(ChannelHandlerContext ctx, HttpRequest request, byte[] data, String contentType) { |
149 | | - DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); |
150 | | - response.headers().add("Content-Type", contentType); |
151 | | - response.content().writeBytes(data); |
152 | | - response.headers().add("Content-Length", data.length); |
153 | | - return response; |
154 | | - } |
155 | 191 | } |
| 192 | + |
0 commit comments