Skip to content

Commit c0c0c2b

Browse files
authored
Merge pull request #15 from zenuo/feature_lints
Feature lints
2 parents 19411d6 + 1f6944e commit c0c0c2b

16 files changed

+208
-101
lines changed

gogo-server/pom.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>zenuo</groupId>
88
<artifactId>gogo</artifactId>
9-
<version>1.7.4</version>
9+
<version>1.7.5</version>
1010

1111
<packaging>jar</packaging>
1212

gogo-server/src/main/java/zenuo/gogo/core/Handler.java

+17-7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import zenuo.gogo.core.processor.IIndexProcessor;
1515
import zenuo.gogo.core.processor.ILintProcessor;
1616
import zenuo.gogo.core.processor.ISearchProcessor;
17+
import zenuo.gogo.core.processor.IStaticProcessor;
1718

1819
import java.net.URLDecoder;
1920
import java.nio.charset.StandardCharsets;
@@ -47,6 +48,11 @@ public final class Handler extends SimpleChannelInboundHandler<FullHttpRequest>
4748
*/
4849
private final ILintProcessor lintProcessor = ServiceLoader.load(ILintProcessor.class).iterator().next();
4950

51+
/**
52+
* 静态文件处理器
53+
*/
54+
private final IStaticProcessor staticProcessor = ServiceLoader.load(IStaticProcessor.class).iterator().next();
55+
5056
/**
5157
* 处理工作者线程池
5258
*/
@@ -73,7 +79,7 @@ protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request)
7379
indexProcessor.response(ctx,
7480
request,
7581
ResponseType.API,
76-
"{\"error\": \"too many requests, please try again later.\"}",
82+
"{\"error\": \"too many requests, please try again later.\"}".getBytes(StandardCharsets.UTF_8),
7783
HttpResponseStatus.TOO_MANY_REQUESTS);
7884
} else {
7985
// 此处线程是事件循环worker,由processWorkers完成逻辑、搜索请求解析,再由processWorkers触发worker线程响应客户端
@@ -88,7 +94,7 @@ private void doChannelRead0(ChannelHandlerContext ctx, FullHttpRequest request)
8894
indexProcessor.response(ctx,
8995
request,
9096
ResponseType.API,
91-
"{\"error\", \"the http method should be GET only\"}",
97+
"{\"error\", \"the http method should be GET only\"}".getBytes(StandardCharsets.UTF_8),
9298
HttpResponseStatus.BAD_REQUEST);
9399
} else {
94100
final QueryStringDecoder decoder = new QueryStringDecoder(request.uri());
@@ -112,11 +118,15 @@ private void doChannelRead0(ChannelHandlerContext ctx, FullHttpRequest request)
112118
lintProcessor.process(ctx, request, decoder, null);
113119
break;
114120
default:
115-
indexProcessor.response(ctx,
116-
request,
117-
ResponseType.API,
118-
"{\"error\": \"BAD_GATEWAY\"}",
119-
HttpResponseStatus.BAD_GATEWAY);
121+
if (decoder.path().startsWith("/static")) {
122+
staticProcessor.process(ctx, request, decoder, null);
123+
} else {
124+
indexProcessor.response(ctx,
125+
request,
126+
ResponseType.API,
127+
"{\"error\": \"BAD_GATEWAY\"}".getBytes(StandardCharsets.UTF_8),
128+
HttpResponseStatus.BAD_GATEWAY);
129+
}
120130
}
121131
}
122132
}

gogo-server/src/main/java/zenuo/gogo/core/config/ApplicationConfig.java

-4
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,6 @@ public class ApplicationConfig {
3333
INSTANCE.gogoConfig.postConstruct();
3434
}
3535

36-
public static void main(String[] args) {
37-
System.out.println(INSTANCE);
38-
}
39-
4036
public static HttpClientConfig httpClientConfig() {
4137
return INSTANCE.httpClientConfig;
4238
}

gogo-server/src/main/java/zenuo/gogo/core/processor/IProcessor.java

+2-4
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
import io.netty.handler.codec.http.QueryStringDecoder;
1111
import zenuo.gogo.core.ResponseType;
1212

13-
import java.nio.charset.StandardCharsets;
14-
1513
interface IProcessor {
1614

1715
/**
@@ -37,14 +35,14 @@ default void response(
3735
final ChannelHandlerContext ctx,
3836
final FullHttpRequest request,
3937
final ResponseType responseType,
40-
final String body,
38+
final byte[] body,
4139
final HttpResponseStatus status
4240
) {
4341
//响应对象
4442
final DefaultFullHttpResponse response = new DefaultFullHttpResponse(
4543
request.protocolVersion(),
4644
status,
47-
body == null ? Unpooled.buffer() : Unpooled.copiedBuffer(body.getBytes(StandardCharsets.UTF_8)));
45+
body == null ? Unpooled.buffer() : Unpooled.copiedBuffer(body));
4846
//设置头信息
4947
response.headers().add(HttpHeaderNames.SERVER, "gogo");
5048
response.headers().add(HttpHeaderNames.CACHE_CONTROL, "private, max-age=120");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package zenuo.gogo.core.processor;
2+
3+
/**
4+
* note
5+
*
6+
* @author zenuo
7+
* @version 2020-07-26
8+
*/
9+
public interface IStaticProcessor extends IProcessor {
10+
}

gogo-server/src/main/java/zenuo/gogo/core/processor/impl/IndexProcessorImpl.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@
99
import zenuo.gogo.web.IIndexPageBuilder;
1010
import zenuo.gogo.web.IPageBuilder;
1111

12+
import java.nio.charset.StandardCharsets;
1213
import java.util.ServiceLoader;
1314

1415
public final class IndexProcessorImpl implements IIndexProcessor {
1516

17+
private static final byte[] RESPONSE_BODY_WELCOME = "{\"info\":\"Hello, welcome to Gogo API, https://github.com/zenuo/gogo\"}".getBytes(StandardCharsets.UTF_8);
18+
1619
/**
1720
* 页面构建器
1821
*/
@@ -24,7 +27,7 @@ public void process(ChannelHandlerContext ctx, FullHttpRequest request, QueryStr
2427
response(ctx,
2528
request,
2629
ResponseType.API,
27-
"{\"info\":\"Hello, welcome to Gogo API, https://github.com/zenuo/gogo\"}",
30+
RESPONSE_BODY_WELCOME,
2831
HttpResponseStatus.OK);
2932
} else {
3033
response(ctx,

gogo-server/src/main/java/zenuo/gogo/core/processor/impl/LintProcessorImpl.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,23 @@
2525
@Slf4j
2626
public final class LintProcessorImpl implements ILintProcessor {
2727

28+
final byte[] RESPONSE_BODY_KEYWORD_EMPTY = "{\"error\": \"the keyword should not be empty\"}".getBytes(StandardCharsets.UTF_8);
29+
2830
@Override
2931
public void process(ChannelHandlerContext ctx, FullHttpRequest request, QueryStringDecoder decoder, ResponseType responseType) {
3032
final List<String> keys = decoder.parameters().get("q");
3133
if (keys == null || "".equals(keys.get(0))) {
3234
response(ctx,
3335
request,
3436
ResponseType.API,
35-
"{\"error\": \"the keyword should not be empty\"}",
37+
RESPONSE_BODY_KEYWORD_EMPTY,
3638
HttpResponseStatus.BAD_REQUEST);
3739
} else {
3840
final LintResponse response = response(keys.get(0));
3941
response(ctx,
4042
request,
4143
ResponseType.API,
42-
JsonUtils.toJson(response),
44+
JsonUtils.toJsonBytes(response),
4345
response.getStatus());
4446
}
4547
}

gogo-server/src/main/java/zenuo/gogo/core/processor/impl/SearchProcessorImpl.java

+7-3
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import zenuo.gogo.web.IPageBuilder;
1616
import zenuo.gogo.web.IResultPageBuilder;
1717

18+
import java.nio.charset.StandardCharsets;
1819
import java.util.Comparator;
1920
import java.util.List;
2021
import java.util.Optional;
@@ -34,14 +35,17 @@ public final class SearchProcessorImpl implements ISearchProcessor {
3435
.sorted(Comparator.comparingInt(ISearchResultProvider::priority))
3536
.collect(Collectors.toList());
3637

38+
private static final byte[] RESPONSE_BODY_KEYWORD_EMPTY = "{\"error\": \"the keyword should not be empty\"}".getBytes(StandardCharsets.UTF_8);
39+
3740
@Override
3841
public void process(ChannelHandlerContext ctx, FullHttpRequest request, QueryStringDecoder decoder, ResponseType responseType) {
3942
final List<String> keys = decoder.parameters().get("q");
4043
if (keys == null || "".equals(keys.get(0))) {
44+
4145
response(ctx,
4246
request,
4347
ResponseType.API,
44-
"{\"error\": \"the keyword should not be empty\"}",
48+
RESPONSE_BODY_KEYWORD_EMPTY,
4549
HttpResponseStatus.BAD_REQUEST);
4650
} else {
4751
final List<String> pages = decoder.parameters().get("p");
@@ -85,15 +89,15 @@ public void process(ChannelHandlerContext ctx, FullHttpRequest request, QueryStr
8589
response(ctx,
8690
request,
8791
responseType,
88-
responseType == ResponseType.API ? "{\"error\": \"" + searchException.getMessage() + "\"}"
92+
responseType == ResponseType.API ? ("{\"error\": \"" + searchException.getMessage() + "\"}").getBytes(StandardCharsets.UTF_8)
8993
: resultPageBuilder.build(SearchResponse.builder().key(key).error(searchException.getMessage()).build()),
9094
HttpResponseStatus.OK);
9195

9296
} else {
9397
response(ctx,
9498
request,
9599
responseType,
96-
responseType == ResponseType.API ? JsonUtils.toJson(response) : resultPageBuilder.build(response),
100+
responseType == ResponseType.API ? JsonUtils.toJsonBytes(response) : resultPageBuilder.build(response),
97101
response.getStatus() == null ? HttpResponseStatus.OK : response.getStatus());
98102
}
99103
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package zenuo.gogo.core.processor.impl;
2+
3+
import io.netty.buffer.Unpooled;
4+
import io.netty.channel.ChannelFutureListener;
5+
import io.netty.channel.ChannelHandlerContext;
6+
import io.netty.handler.codec.http.DefaultFullHttpResponse;
7+
import io.netty.handler.codec.http.FullHttpRequest;
8+
import io.netty.handler.codec.http.HttpHeaderNames;
9+
import io.netty.handler.codec.http.HttpHeaderValues;
10+
import io.netty.handler.codec.http.HttpResponseStatus;
11+
import io.netty.handler.codec.http.QueryStringDecoder;
12+
import lombok.extern.slf4j.Slf4j;
13+
import org.apache.commons.io.IOUtils;
14+
import zenuo.gogo.core.ResponseType;
15+
import zenuo.gogo.core.processor.IStaticProcessor;
16+
17+
import java.io.InputStream;
18+
19+
/**
20+
* note
21+
*
22+
* @author zenuo
23+
* @version 2020-07-26
24+
*/
25+
@Slf4j
26+
public final class StaticProcessorImpl implements IStaticProcessor {
27+
@Override
28+
public void process(ChannelHandlerContext ctx, FullHttpRequest request, QueryStringDecoder decoder, ResponseType responseType) {
29+
final String path = "web" + decoder.path();
30+
final byte[] bytes;
31+
try (final InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream(path)) {
32+
bytes = IOUtils.toByteArray(inputStream);
33+
} catch (Exception e) {
34+
log.error("process static '{}' error", path, e);
35+
throw new RuntimeException(e);
36+
}
37+
//响应对象
38+
final DefaultFullHttpResponse response = new DefaultFullHttpResponse(
39+
request.protocolVersion(),
40+
HttpResponseStatus.OK,
41+
Unpooled.copiedBuffer(bytes));
42+
//设置头信息
43+
response.headers().add(HttpHeaderNames.SERVER, "gogo");
44+
response.headers().add(HttpHeaderNames.CACHE_CONTROL, "private, max-age=120");
45+
if (path.endsWith(".js")) {
46+
response.headers().add(HttpHeaderNames.CONTENT_TYPE, "text/javascript");
47+
} else {
48+
response.headers().add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.FILE);
49+
}
50+
//响应后关闭通道
51+
ctx.writeAndFlush(response)
52+
.addListener(ChannelFutureListener.CLOSE);
53+
}
54+
}

gogo-server/src/main/java/zenuo/gogo/util/JsonUtils.java

+9
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ public static String toJson(final Object object) {
2929
}
3030
}
3131

32+
public static byte[] toJsonBytes(final Object object) {
33+
try {
34+
return Constants.MAPPER.writeValueAsBytes(object);
35+
} catch (Exception e) {
36+
log.error("toJson", e);
37+
throw new IllegalStateException(e);
38+
}
39+
}
40+
3241
public static <T> T fromJson(String json, Class<? extends T> clazz) {
3342
try {
3443
return Constants.MAPPER.readValue(json, clazz);

gogo-server/src/main/java/zenuo/gogo/web/IPageBuilder.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ public interface IPageBuilder {
1515
* @param response 响应
1616
* @return 网页的HTML字符串
1717
*/
18-
String build(final IResponse response);
18+
byte[] build(final IResponse response);
1919
}
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,36 @@
11
package zenuo.gogo.web;
22

3+
import lombok.extern.slf4j.Slf4j;
4+
import org.apache.commons.io.IOUtils;
35
import zenuo.gogo.core.config.ApplicationConfig;
46
import zenuo.gogo.core.config.GogoConfig;
57
import zenuo.gogo.model.IResponse;
68

7-
import java.time.LocalTime;
9+
import java.io.InputStream;
10+
import java.nio.charset.StandardCharsets;
11+
import java.util.Objects;
812

913
/**
1014
* 主页构建器
1115
*
1216
* @author zenuo
1317
* 2018-07-08 20:50:25
1418
*/
19+
@Slf4j
1520
public final class IndexPageBuilder implements IIndexPageBuilder {
1621

1722
private final GogoConfig gogoConfig = ApplicationConfig.gogoConfig();
1823

19-
/**
20-
* 样式表之前的HTML字符串
21-
*/
22-
private static final String HTML_BEFORE_STYLE
23-
= "<!DOCTYPE html>" +
24-
"<html lang=\"en\">" +
25-
"<head>" +
26-
"<meta charset=\"utf-8\"/>" +
27-
"<title>勾勾</title>" +
28-
"<style>";
24+
private final byte[] htmlBytes;
2925

30-
/**
31-
* 样式表之后的HTML字符串
32-
*/
33-
private String htmlAfterStyle;
34-
35-
/**
36-
* 夜间模式的样式表
37-
*/
38-
private static final String HTML_NIGHT_MODE_STYLE = "body{text-align:center;background-color:#000;color:#B6C5D4}" +
39-
"h1{font-size:50px;font-family:\"Times New Roman\",Times,serif}" +
40-
"footer{font-size:15px;font-family:Roboto,arial,sans-serif}" +
41-
".main{margin:0 auto;width:50%;padding-bottom:50px}";
42-
43-
/**
44-
* 日间模式的样式表
45-
*/
46-
private static final String HTML_DAY_MODE_STYLE = "body{text-align:center;background-color:#F8F4E7;color:#552800}" +
47-
"h1{font-size:50px;font-family:\"Times New Roman\",Times,serif}" +
48-
"footer{font-size:15px;font-family:Roboto,arial,sans-serif}" +
49-
".main{margin:0 auto;width:50%;padding-bottom:50px}";
50-
51-
public IndexPageBuilder() {
52-
htmlAfterStyle = "</style>" +
53-
"</head>" +
54-
"<body>" +
55-
"<a href=\"https://github.com/zenuo/gogo\"><img style=\"position: absolute; top: 0; right: 0; border: 0;\" width=\"149\" height=\"149\" src=\"https://github.blog/wp-content/uploads/2008/12/forkme_right_orange_ff7600.png?resize=149%2C149\" class=\"attachment-full size-full\" alt=\"Fork me on GitHub\" data-recalc-dims=\"1\"></a>" +
56-
"<div class=\"main\">" +
57-
"<h1>勾勾</h1>" +
58-
"<form action=\"/search\" method=\"GET\" onsubmit=\"return q.value!=''\">" +
59-
"<input name=\"q\" autocomplete=\"off\" autofocus=\"autofocus\" type=\"text\">" +
60-
"<button value=\"Search\" type=\"submit\">Go</button>" +
61-
"</form>" +
62-
"</div>" +
63-
"<footer>"
64-
+ gogoConfig.getSlogan()
65-
+ "</footer>" +
66-
"</body>" +
67-
"</html>";
26+
{
27+
try (final InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("web/index.html")) {
28+
final String indexHtml = IOUtils.toString(Objects.requireNonNull(resourceAsStream), StandardCharsets.UTF_8);
29+
htmlBytes = indexHtml.replace("__SLOGAN__", gogoConfig.getSlogan()).getBytes(StandardCharsets.UTF_8);
30+
} catch (Exception e) {
31+
log.error("build index error", e);
32+
throw new RuntimeException(e);
33+
}
6834
}
6935

7036
/**
@@ -73,22 +39,8 @@ public IndexPageBuilder() {
7339
* @return 主页的字符串
7440
*/
7541
@Override
76-
public String build(IResponse response) {
77-
//字符串构建器,初始化内容为样式表之前的HTML
78-
final StringBuilder sb = new StringBuilder(HTML_BEFORE_STYLE);
79-
//当前时间
80-
final LocalTime now = LocalTime.now();
81-
if (now.isBefore(gogoConfig.getDayModeStartTime()) ||
82-
now.isAfter(gogoConfig.getDayModeEndTime())) {
83-
//若是夜间模式,拼接夜间模式样式表
84-
sb.append(HTML_NIGHT_MODE_STYLE);
85-
} else {
86-
//若是日间模式,拼接日间模式样式表
87-
sb.append(HTML_DAY_MODE_STYLE);
88-
}
89-
//拼接样式表之后的HTML
90-
sb.append(htmlAfterStyle);
42+
public byte[] build(IResponse response) {
9143
//返回字符串
92-
return sb.toString();
44+
return htmlBytes;
9345
}
9446
}

0 commit comments

Comments
 (0)