Skip to content

Commit 8563aab

Browse files
author
harry
committed
完善内置http服务器
1 parent 3242238 commit 8563aab

File tree

3 files changed

+169
-86
lines changed

3 files changed

+169
-86
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
*
3+
* MIT License
4+
*
5+
* Copyright (c) 2017 朱辉 https://blog.yeetor.com
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in all
15+
* copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23+
* SOFTWARE.
24+
*
25+
*/
26+
27+
package com.yeetor.server;
28+
29+
import java.lang.annotation.Retention;
30+
import java.lang.annotation.RetentionPolicy;
31+
32+
@Retention(RetentionPolicy.RUNTIME)
33+
public @interface HttpRouter {
34+
/**
35+
* 路由的路径
36+
* @return
37+
*/
38+
String uri() default "/";
39+
}

src/main/java/com/yeetor/server/HttpServer.java

Lines changed: 113 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
import com.yeetor.adb.AdbUtils;
3030
import com.yeetor.minicap.Minicap;
3131
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;
3335
import io.netty.channel.ChannelFutureListener;
3436
import io.netty.channel.ChannelHandlerContext;
3537
import io.netty.handler.codec.http.*;
@@ -38,6 +40,9 @@
3840

3941
import javax.activation.MimetypesFileTypeMap;
4042
import java.io.*;
43+
import java.lang.annotation.Annotation;
44+
import java.lang.reflect.InvocationTargetException;
45+
import java.lang.reflect.Method;
4146

4247
import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE;
4348
import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;
@@ -46,110 +51,142 @@
4651

4752
public class HttpServer {
4853
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() {
5257

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-
}
6058
}
6159

62-
public HttpResponse shot(ChannelHandlerContext ctx, HttpRequest request) {
63-
60+
public void onRequest(ChannelHandlerContext ctx, HttpRequest request, HttpResponse response) {
6461
String uri = request.uri();
65-
String[] args = uri.split("/");
62+
logger.info("http:" + uri);
6663

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+
}
6982
}
7083

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);
8185
}
8286

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) {
9088
String location = request.uri().substring(1);
9189
if (location.equals("")) {
92-
location = "index.html";
90+
location = INDEX_FILE;
9391
}
94-
92+
9593
File localFile = new File(Constant.getResourceDir(), "web" + File.separator + location);
96-
94+
9795
if (!localFile.exists() || localFile.isHidden()) {
98-
return doDefaultResponse(ctx, request, "404".getBytes(), "text/plain");
96+
writeErrorHttpResponse(ctx, request, response, HttpResponseStatus.NOT_FOUND);
97+
return;
9998
}
100-
101-
ChunkedFile chunkedFile = null;
99+
102100
try{
103101
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+
105113
}catch (IOException e) {
106-
e.printStackTrace();
114+
writeErrorHttpResponse(ctx, request, response, HttpResponseStatus.INTERNAL_SERVER_ERROR);
115+
return;
107116
}
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+
}
114118

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+
}
116126

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+
}
117142

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());
121163
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);
126172
}
127173

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) {
132175

133-
String json = "";
134-
byte[] data = json.getBytes();
176+
if (HttpUtil.isKeepAlive(request)) {
177+
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
178+
}
135179

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);
139185

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);
145188
}
146189
}
147190

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-
}
155191
}
192+

src/main/java/com/yeetor/server/handler/HTTPHandler.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,20 +58,27 @@ protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Except
5858
}
5959

6060
HttpRequest request = (HttpRequest) msg;
61+
62+
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
6163
boolean isKeepAlive = HttpUtil.isKeepAlive(request);
64+
6265

63-
HttpResponse response = server.onRequest(ctx, request);
64-
if (response == null) {
65-
return;
66-
}
6766
response.headers().add("Access-Control-Allow-Origin", "*");
6867
response.headers().add("Server", "Yeetor");
69-
if (isKeepAlive) {
70-
response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
71-
} else {
72-
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
73-
}
74-
ctx.writeAndFlush(response);
68+
69+
server.onRequest(ctx, request, response);
70+
71+
// HttpResponse response = server.onRequest(ctx, request);
72+
// if (response == null) {
73+
// return;
74+
// }
75+
76+
// if (isKeepAlive) {
77+
// response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
78+
// } else {
79+
// ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
80+
// }
81+
// ctx.writeAndFlush(response);
7582
}
7683
}
7784

0 commit comments

Comments
 (0)