AndServer源码分析与学习

1,020 阅读10分钟

AndServer介绍

Android平台的webserver和Web框架,支持静态网站部署、动态Http接口、反向代理服务器等。

源码地址:github.com/yanzhenjie/…

文档地址: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等处理。

层次架构图

官方文档给出如下层次架构图蓝图: image.png

其中

  • socket层: 使用ServerSocket
  • HttpParse: 通过 Apache httpcore 实现
  • FrameWork 以及Handler 为AndServer核心部分 。主要实现:请求处理、内容分发、拦截器以及session,cookie,cache等

运行时流程图

system_flow_chat.gif

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.sendResponseHeaderconn.sendResponseEntity

WebFramework

上面我们层提到整体层次架构中有个Framework层,这层也是AndServer的核心所在,接下来我们对这层进行分析。

应用层架构图

应用层架构如下图所示: image.png

  • Dispatcher:分发
  • Interceptor:拦截
  • HandlerAdapter:handler适配器
  • Handler:实际处理逻辑
  • ViewResolver:转换生成response

应用层运行时流程图

下图是应用层运行的流程图: framework_flow_chat.gif

我们再根据实际代码逻辑总结出如下方法调用流程图:

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后就可以访问相应路径下的资源:

image.png

上述过程用到了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中