AndServer介绍
Android平台的webserver和Web框架,支持静态网站部署、动态Http接口、反向代理服务器等。
文档地址:yanzhenjie.github.io/AndServer/
特性:
- 部署静态网站
- 动态 HTTP API
- 全局请求拦截器
- 全局异常处理器
- 全局消息转换器
AndServer使用
使用非常简单,构造器模式创建Server实例,然后调用startup和shutdown进行启动和关闭
// 构造server
Server server = AndServer.webServer(context)
.port(8080)
.timeout(10, TimeUnit.SECONDS)
.build();
// startup the server.
server.startup();
// shutdown the server.
server.shutdown();
更多用法参见AndServer
AndServer整体架构
AndServer底层主要依赖ServerSocket和Apache HttpCore,主要用于接收和解析请求。 AndServer的核心是WebFramework的实现,主要用户分发处理拦截,以及进行Session,Cookie,Cache等处理。
层次架构图
官方文档给出如下层次架构图蓝图:
其中
- socket层: 使用ServerSocket
- HttpParse: 通过 Apache httpcore 实现
- FrameWork 以及Handler 为AndServer核心部分 。主要实现:请求处理、内容分发、拦截器以及session,cookie,cache等
运行时流程图
ServerSocket创建过程和Http解析过程
graph TD
Webserver.startup --> 创建ServerSocket绑定本机ip和端口 --> ServerSocket.accept
WebServer即我们在上面创建的实例,Server的启动方法具体如下:
@Override
public void startup() {
if (isRunning) {
return;
}
Executors.getInstance().execute(new Runnable() {
@Override
public void run() {
try {
// 关键代码1:创建HttpServer
mHttpServer = createHttpSerVer();
// 关键代码2:启动HttpServer
mHttpServer.start();
isRunning = true;
// 监听回掉,异常处理等
...
} catch (final Exception e) {
// 监听回掉,异常处理等
...
}
}
});
}
其中关键部分是创建HttpServer并启动,创建部分如下:
private HttpServer createHttpSerVer() {
return ServerBootstrap.bootstrap()
.setServerSocketFactory(mSocketFactory)
.setSocketConfig(
SocketConfig.custom()
.setSoKeepAlive(true)
.setSoReuseAddress(true)
.setTcpNoDelay(true)
.setSoTimeout(mTimeout)
.setBacklogSize(BUFFER)
.setRcvBufSize(BUFFER)
.setSndBufSize(BUFFER)
.setSoLinger(0)
.build()
)
.setLocalAddress(mInetAddress)
.setListenerPort(mPort)
.setSslContext(mSSLContext)
.setSslSetupHandler(new SSLSetup(mSSLSocketInitializer))
.setServerInfo(AndServer.INFO)
// 关键代码: 注册HttpRequestHandler
.registerHandler("*", requestHandler())
.setExceptionLogger(ExceptionLogger.NO_OP)
.create();
}
创建方法中大部分是一些配置数据初始化及透传等,我们先不关注这些数据的具体细节,需要关注的只有registerHandler()这个方法,主要作用是注册HttpRequestHandler,requestHandler()的具体实现我们在下面再说。
HttpServer启动过程如下,主要是创建serverSocket对象,并进行ip和端口号的绑定工作。
public void start() throws IOException {
if (this.status.compareAndSet(Status.READY, Status.ACTIVE)) {
//关键代码1: 创建ServerSocket
this.serverSocket = this.serverSocketFactory.createServerSocket();
this.serverSocket.setReuseAddress(this.socketConfig.isSoReuseAddress());
//关键代码2: 绑定本机ip和端口
this.serverSocket.bind(new InetSocketAddress(this.ifAddress, this.port),
this.socketConfig.getBacklogSize());
if (this.socketConfig.getRcvBufSize() > 0) {
this.serverSocket.setReceiveBufferSize(this.socketConfig.getRcvBufSize());
}
if (this.sslSetupHandler != null && this.serverSocket instanceof SSLServerSocket) {
this.sslSetupHandler.initialize((SSLServerSocket) this.serverSocket);
}
// 构造RequestListener
this.requestListener = new RequestListener(
this.socketConfig,
this.serverSocket,
this.httpService,
this.connectionFactory,
this.exceptionLogger,
this.workerExecutorService);
this.listenerExecutorService.execute(this.requestListener);
}
}
serverSocket创建创建好后,会构造一个requestListener对象,并放入线程池中执行。
RequestListener运行时,调用serversocket的accept()方法,这个方法会一直阻塞直到接收到请求,之后会返回socket对象,如下关键代码1。
@Override
public void run() {
try {
while (!isTerminated() && !Thread.interrupted()) {
// 关键代码1: Listens for a connection to be made to this
// socket and accepts it. The method blocks until a connection
// is made.
final Socket socket = this.serversocket.accept();
socket.setSoTimeout(this.socketConfig.getSoTimeout());
socket.setKeepAlive(this.socketConfig.isSoKeepAlive());
socket.setTcpNoDelay(this.socketConfig.isTcpNoDelay());
if (this.socketConfig.getRcvBufSize() > 0) {
socket.setReceiveBufferSize(this.socketConfig.getRcvBufSize());
}
if (this.socketConfig.getSndBufSize() > 0) {
socket.setSendBufferSize(this.socketConfig.getSndBufSize());
}
if (this.socketConfig.getSoLinger() >= 0) {
socket.setSoLinger(true, this.socketConfig.getSoLinger());
}
// 关键代码2: 根据socket构造 HttpServerConnection
final HttpServerConnection conn = this.connectionFactory.createConnection(socket);
final Worker worker = new Worker(this.httpService, conn, this.exceptionLogger);
// 执行work
this.executorService.execute(worker);
}
} catch (final Exception ex) {
this.exceptionLogger.log(ex);
}
}
生成socket对象后,根据socket对象创建HttpServerConnection对象,并放入Worker中执行。 Worker类运行细节如下:
@Override
public void run() {
try {
final BasicHttpContext localContext = new BasicHttpContext();
final HttpCoreContext context = HttpCoreContext.adapt(localContext);
while (!Thread.interrupted() && this.conn.isOpen()) {
// 关键代码1:通过httpservice的handleRequest方法处理conn
this.httpservice.handleRequest(this.conn, context);
localContext.clear();
}
// 关键代码2:关闭conn
this.conn.close();
} catch (final Exception ex) {
this.exceptionLogger.log(ex);
} finally {
try {
this.conn.shutdown();
} catch (final IOException ex) {
this.exceptionLogger.log(ex);
}
}
}
上述代码1中会调用httpservice的handleRequest,用于处理connection的request和返回response,具体实现如下
/**
* Handles receives one HTTP request over the given connection within the
* given execution context and sends a response back to the client.
*/
public void handleRequest(
final HttpServerConnection conn,
final HttpContext context) throws IOException, HttpException {
context.setAttribute(HttpCoreContext.HTTP_CONNECTION, conn);
HttpRequest request = null;
HttpResponse response = null;
try {
// 关键代码1:接收request
request = conn.receiveRequestHeader();
context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
...
if (response == null) {
// 关键代码2:创建response
response = this.responseFactory.newHttpResponse(HttpVersion.HTTP_1_1,
HttpStatus.SC_OK, context);
this.processor.process(request, context);
// 关键代码3:执行HttpRequestHandler --- 后续再讲
doService(request, response, context);
}
...
} catch (final HttpException ex) {
response = this.responseFactory.newHttpResponse
(HttpVersion.HTTP_1_0, HttpStatus.SC_INTERNAL_SERVER_ERROR,
context);
handleException(ex, response);
}
context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
this.processor.process(response, context);
// send
conn.sendResponseHeader(response);
if (canResponseHaveBody(request, response)) {
conn.sendResponseEntity(response);
}
conn.flush();
if (!this.connStrategy.keepAlive(response, context)) {
conn.close();
}
}
此处我们不关注接收request和创建response对象的具体细节,我们只看拿到这两个对象之后,会调用doService方法。至此request和response对象就进一步处理,此处我们先不看具体处理过程,处理完成后再调用 HttpServerConnection的发送方法。之后客户端就能收到response消息了。
conn.sendResponseHeader和conn.sendResponseEntity
WebFramework
上面我们层提到整体层次架构中有个Framework层,这层也是AndServer的核心所在,接下来我们对这层进行分析。
应用层架构图
应用层架构如下图所示:
- Dispatcher:分发
- Interceptor:拦截
- HandlerAdapter:handler适配器
- Handler:实际处理逻辑
- ViewResolver:转换生成response
应用层运行时流程图
下图是应用层运行的流程图:
我们再根据实际代码逻辑总结出如下方法调用流程图:
graph TD
DispatcherHandler.handler --> 获取需要拦截的适配器HandlerAdapter --> 获取处理器RequestHandler --> 拦截器处理如缓存拦截 --> RequestHandler.handle --> View封装ResponseBody --> ViewResolver生成Response
我们在讲Webserver启动过程中层提到之后会讲的一个方法,
registerHandler("*", requestHandler())
这个方法主要是注册HttpRequestHandler,而requestHandler()是在WebServer实现的方法,如下:
@Override
protected HttpRequestHandler requestHandler() {
DispatcherHandler handler = new DispatcherHandler(mContext);
ComponentRegister register = new ComponentRegister(mContext);
try {
//此处后续会讲
register.register(handler, mGroup);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
return handler;
}
而DispatcherHandler实现了HttpRequestHandler
public class DispatcherHandler implements HttpRequestHandler, Register {}
registerHandler的具体实现是将handler放到一个map中
public final ServerBootstrap registerHandler(final String pattern, final HttpRequestHandler handler) {
if (pattern == null || handler == null) {
return this;
}
if (handlerMap == null) {
handlerMap = new HashMap<String, HttpRequestHandler>();
}
handlerMap.put(pattern, handler);
return this;
}
最终会传到HttpService中的HttpRequestHandlerMapper对象中,具体过程我们此处先不关心。
我们在讲整体架构时,在讲到doService()后并未继续向下讲,接下来主要讲方法doService()的具体实现
protected void doService(
final HttpRequest request,
final HttpResponse response,
final HttpContext context) throws HttpException, IOException {
HttpRequestHandler handler = null;
if (this.handlerMapper != null) {
// 关键代码1:从handlerMapper中根据当前request获取对应HttpRequestHandler
handler = this.handlerMapper.lookup(request);
}
if (handler != null) {
// 关键代码2:执行handle方法
handler.handle(request, response, context);
} else {
response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED);
}
}
首先从handlerMapper(上面提过的HttpRequestHandlerMapper对象)中根据当前request获取对应HttpRequestHandler对象handler;然后在调用handler的handle方法。
我们再看handle中,此处实际逻辑在我们实现的DispatcherHandler类中
@Override
public void handle(org.apache.httpcore.HttpRequest req, org.apache.httpcore.HttpResponse res,
org.apache.httpcore.protocol.HttpContext con) {
HttpRequest request = new StandardRequest(req, new StandardContext(con), this, mSessionManager);
HttpResponse response = new StandardResponse(res);
// 调用handle方法
handle(request, response);
}
private void handle(HttpRequest request, HttpResponse response) {
MultipartResolver multipartResolver = new StandardMultipartResolver();
try {
if (multipartResolver.isMultipart(request)) {
configMultipart(multipartResolver);
request = multipartResolver.resolveMultipart(request);
}
// 关键代码1:Determine adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(request);
if (ha == null) {
throw new NotFoundException(request.getPath());
}
// 关键代码2:Determine handler for the current request.
RequestHandler handler = ha.getHandler(request);
if (handler == null) {
throw new NotFoundException(request.getPath());
}
// 关键代码3:Pre processor, e.g. interceptor.
if (preHandle(request, response, handler)) {
return;
}
// Actually invoke the handler.
request.setAttribute(HttpContext.ANDROID_CONTEXT, mContext);
request.setAttribute(HttpContext.HTTP_MESSAGE_CONVERTER, mConverter);
// 关键代码4:获取view对象
View view = handler.handle(request, response);
// 关键代码5:解析view并转换为http包内容
mViewResolver.resolve(view, request, response);
processSession(request, response);
} catch (Throwable err) {
try {
// 关键代码6:异常处理
mResolver.onResolve(request, response, err);
} catch (Exception e) {
e = new ServerInternalException(e);
response.setStatus(StatusCode.SC_INTERNAL_SERVER_ERROR);
response.setBody(new StringBody(e.getMessage()));
}
processSession(request, response);
} finally {
if (request instanceof MultipartRequest) {
multipartResolver.cleanupMultipart((MultipartRequest) request);
}
}
}
上述过程主要分为6部分对request和response进行处理,我们此处以一个例子讲解处理过程:静态资源部署的过程。
实现静态资源部署在应用层只需做如下设置,后续启动AndServer后就可以访问相应路径下的资源:
上述过程用到了Website,我们通过StorageWebsite看下Website是什么:
public class StorageWebsite extends BasicWebsite implements Patterns {}
public abstract class BasicWebsite extends Website {}
public abstract class Website implements HandlerAdapter, ETag, LastModified {}
由此可见Website其实是实现了HandlerAdapter。 HandlerAdapter结构如下:
public interface HandlerAdapter {
/**
* Whether to intercept the current request.
*/
boolean intercept(@NonNull HttpRequest request);
/**
* Get the handler that handles the current request.
*/
@Nullable
RequestHandler getHandler(@NonNull HttpRequest request);
}
HandlerAdapter中通过getHandler获取RequestHandler。
我们继续看通过注解标记的WebConfig,注解生成的注册类ConfigRegister(实现OnRegister类)中会有个onRegister方法:
@Override
public void onRegister(Context context, String group, Register register) {
WebConfig config = mMap.get(group);
if(config == null) {
config = mMap.get("default");
}
if(config != null) {
Delegate delegate = Delegate.newInstance();
// 关键代码1:上面代码中onConfig时机
config.onConfig(context, delegate);
List<Website> list = delegate.getWebsites();
if(list != null && !list.isEmpty()) {
for (Website website : list) {
// 关键代码2:添加适配器 将website添加到register中
register.addAdapter(website);
}
}
Multipart multipart = delegate.getMultipart();
register.setMultipart(multipart);
}
}
上述过程实现了调用WebConfig的onConfig来添加Website,然后再将所有website添加到一个叫register的对象中,我们在往上找看register对象是什么。
可以发现ConfigRegister的onRegister是在ComponentRegister类中调用,具体方法如下:
public void register(Register register, String group)
throws InstantiationException, IllegalAccessException {
AssetManager manager = mContext.getAssets();
String[] pathList = null;
try {
pathList = manager.list("");
} catch (IOException e) {
e.printStackTrace();
}
if (pathList == null || pathList.length == 0) {
return;
}
for (String path: pathList) {
if (path.endsWith(ANDSERVER_REGISTER_SUFFIX)) {
String packageName = path.substring(0, path.lastIndexOf(ANDSERVER_REGISTER_SUFFIX));
for (String clazz: REGISTER_LIST) {
String className = String.format("%s%s%s", packageName, PROCESSOR_PACKAGE, clazz);
// 关键代码2
registerClass(register, group, className);
}
}
}
}
private void registerClass(Register register, String group, String className)
throws InstantiationException, IllegalAccessException {
try {
Class<?> clazz = Class.forName(className);
if (OnRegister.class.isAssignableFrom(clazz)) {
OnRegister load = (OnRegister) clazz.newInstance();
// 关键代码1: 反射获取OnRegister后调用onRegister
load.onRegister(mContext, group, register);
}
} catch (ClassNotFoundException ignored) {
}
}
此处通过反射获取OnRegister类并调用onRegister方法。
所以关键在ComponentRegister, 这个类我们在之前的requestHandler()方法中见到过:
@Override
protected HttpRequestHandler requestHandler() {
DispatcherHandler handler = new DispatcherHandler(mContext);
ComponentRegister register = new ComponentRegister(mContext);
try {
// handler 是作为register传到ComponentRegister中
register.register(handler, mGroup);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
return handler;
}
可以看到上面的register对象就是DispatcherHandler类的实例,到此处已经将website添加到DispatcherHandler中了,我们回到DispatcherHandler的handle方法,第一步是获取HandlerAdapter,并判断是否处理:
// 关键代码1:Determine adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(request);
private HandlerAdapter getHandlerAdapter(HttpRequest request) {
for (HandlerAdapter ha: mAdapterList) {
if (ha.intercept(request)) {
return ha;
}
}
return null;
}
StorageWebsite中判断是否处理:
@Override
public boolean intercept(@NonNull HttpRequest request) {
String httpPath = request.getPath();
File file = findPathFile(httpPath);
return file != null;
}
关键代码1理清楚了。
继续向下,第二步获取RequestHandler
// 关键代码2:Determine handler for the current request.
RequestHandler handler = ha.getHandler(request);
去Website中看,Website实现了getHandler()方法,返回RequestHandler
@Nullable
@Override
public RequestHandler getHandler(@NonNull HttpRequest request) {
return new RequestHandler() {
@Nullable
@Override
public String getETag(@NonNull HttpRequest request) throws Throwable {
return Website.this.getETag(request);
}
@Override
public long getLastModified(@NonNull HttpRequest request) throws Throwable {
return Website.this.getLastModified(request);
}
@Override
public View handle(@NonNull HttpRequest request, @NonNull HttpResponse response) throws Throwable {
return new BodyView(getBody(request, response));
}
};
}
RequestHandler具体结构如下
public interface RequestHandler extends ETag, LastModified {
/**
* Use the given handler to handle this request.
*/
View handle(@NonNull HttpRequest request, @NonNull HttpResponse response) throws Throwable;
}
接下来看第4步,关键代码4:获取view对象
// 关键代码4:获取view对象
View view = handler.handle(request, response);
在website中,通过BodyView包装一下getBody()方法返回值
@Override
public View handle(@NonNull HttpRequest request, @NonNull HttpResponse response) throws Throwable {
return new BodyView(getBody(request, response));
}
所以关键在getBody方法,getBody返回ResponseBody
@NonNull
public abstract ResponseBody getBody(@NonNull HttpRequest request, @NonNull HttpResponse response)
throws IOException;
由此可见View结构是对ResponseBody的包装
我们在StorageWebsite中看下getBody()方法具体实现,实际就是我们本地资源文件
@NonNull
@Override
public ResponseBody getBody(@NonNull HttpRequest request, @NonNull HttpResponse response) throws IOException {
String httpPath = request.getPath();
File targetFile = new File(mRootPath, httpPath);
if (targetFile.exists() && targetFile.isFile()) {
return new FileBody(targetFile);
}
File indexFile = new File(targetFile, getIndexFileName());
if (indexFile.exists() && indexFile.isFile()) {
if (!httpPath.endsWith(File.separator)) {
String redirectPath = addEndSlash(httpPath);
String query = queryString(request);
response.sendRedirect(redirectPath + "?" + query);
return new StringBody("");
}
return new FileBody(indexFile);
}
throw new NotFoundException(httpPath);
}
然后看下第5步关键代码5:解析view并转换为http包内容
// 关键代码5:解析view并转换为http包内容
mViewResolver.resolve(view, request, response);
public void resolve(@Nullable View view, @NonNull HttpRequest request, @NonNull HttpResponse response) {
if (view == null) {
return;
}
// view中获取output
Object output = view.output();
if (view.rest()) {
resolveRest(output, request, response);
} else {
resolvePath(output, request, response);
}
}
private void resolveRest(Object output, @NonNull HttpRequest request, @NonNull HttpResponse response) {
if (output instanceof ResponseBody) {
response.setBody((ResponseBody) output);
} else if (mConverter != null) {
response.setBody(mConverter.convert(output, obtainProduce(request)));
} else if (output == null) {
response.setBody(new StringBody(""));
} else if (output instanceof String) {
response.setBody(new StringBody(output.toString(), obtainProduce(request)));
} else {
response.setBody(new StringBody(output.toString()));
}
}
这步主要将ResponseBody设置到response中。 至此,整个流程就结束了,请求端会收到response。
上面我们跳过了拦截器处理逻辑,拦截器处理流程和HandlerAdapter差不多,也是通过注解加入到DispatcherHandler中,我们着重看下处理拦截示例:
public class ModifiedInterceptor implements HandlerInterceptor {
@Override
public boolean onIntercept(@NonNull HttpRequest request, @NonNull HttpResponse response,
@NonNull RequestHandler handler) {
// Process cache header, if supported by the handler.
HttpMethod method = request.getMethod();
if (method == HttpMethod.GET || method == HttpMethod.HEAD) {
String eTag = null;
try {
// 获取eTag
eTag = handler.getETag(request);
} catch (Throwable e) {
Log.w(AndServer.TAG, e);
}
long lastModified = -1;
try {
// 获取lastModified
lastModified = handler.getLastModified(request);
} catch (Throwable e) {
Log.w(AndServer.TAG, e);
}
return new Modified(request, response).process(eTag, lastModified);
}
return false;
}
}
public boolean process(@Nullable String eTag, long lastModified) {
if (isNotModified) {
return true;
}
// See https://tools.ietf.org/html/rfc7232#section-6
if (validateIfUnmodifiedSince(lastModified)) {
if (!isNotModified) {
mResponse.setStatus(StatusCode.SC_LENGTH_REQUIRED);
}
return isNotModified;
}
// First, prioritized.
boolean validated = validateIfNoneMatch(eTag);
// Second.
if (!validated) {
validateIfModifiedSince(lastModified);
}
// Update response
HttpMethod method = mRequest.getMethod();
boolean isGetHead = (method == HttpMethod.GET || method == HttpMethod.HEAD);
if (isNotModified) {
mResponse.setStatus(isGetHead ? StatusCode.SC_NOT_MODIFIED : StatusCode.SC_LENGTH_REQUIRED);
}
if (isGetHead) {
if (lastModified > 0 && mResponse.getHeader(LAST_MODIFIED) == null) {
mResponse.setDateHeader(LAST_MODIFIED, lastModified);
}
if (!TextUtils.isEmpty(eTag) && mResponse.getHeader(ETAG) == null) {
mResponse.setHeader(ETAG, padETagIfNecessary(eTag));
}
mResponse.setHeader(CACHE_CONTROL, "private");
}
return isNotModified;
}
在Modified中process方法会将eTag和lastModified设置到Response的Header中