JDK自带HttpServer处理Http请求

下面是JDK自带的HttpServer处理Http请求的源码和流程,我看网上貌似还没有介绍这个的流程,所有就画了一下,如有不足,请矫正。

1、官方API

https://docs.oracle.com/javase/7/docs/jre/api/net/httpserver/spec/com/sun/net/httpserver/HttpExchange.html

JDK自带HttpServer处理Http请求_第1张图片

2、启动一个HttpServer demo:

private static final Map routeMap = new HashMap();

 

// 配置要创建的Context, key为请求的路径,value为请求的处理器,AgentServerHandler是实现HttpHandler接口,重写handle方法,处理自己的逻辑;

static  {

    routeMap.put("/agent/server"new AgentServerHandler());

}

 

public static void main(String[] args) throws Exception{

    String port = PropertiesHelper.getProperty("server.port""20200");

    HttpServer.create();

    // 绑定地址,端口,请求队列; 队列设置成0则使用默认值:50

    HttpServer server = HttpServer.create(new InetSocketAddress(Integer.parseInt(port)), 0);

 

    logger.info("Loading route...");

    for (String keySet : routeMap.keySet()) {

        // 可以创建多个Context,每个key对应一个Handler;

        server.createContext(keySet, routeMap.get(keySet));

    }

    // 配置HtteServer请求处理的线程池,没有配置则使用默认的线程池;

    server.setExecutor(YjThreadPool.build());

    server.start();

    logger.info("Server have been started. Listen to port: " + port);

}

Context 可以创建多个,核心思想:HttpServer将path和Handler处理器封装成 HttpContextImpl 对象,然后复制给ServerImpl的属性contexts, 其实就是一个链表;

class ContextList {

    static final int MAX_CONTEXTS = 50;

    LinkedList list = new LinkedList();

 

    ContextList() {

    }

 

    public synchronized void add(HttpContextImpl var1) {

        assert var1.getPath() != null;

        this.list.add(var1);

    }

}

 

 

public synchronized HttpContextImpl createContext(String var1, HttpHandler var2) {

    if (var2 != null && var1 != null) {

        HttpContextImpl var3 = new HttpContextImpl(this.protocol, var1, var2, this);

        // 将path和Handler封装在var3中,add到contexts链表中;

        this.contexts.add(var3);

        this.logger.config("context created: " + var1);

        return var3;

    else {

        throw new NullPointerException("null handler, or path parameter");

    }

}

 

 

removeContext 操作实质就是从LinkedList中取元素;

HttpServer处理Http请求的流程

流程图如下:

JDK自带HttpServer处理Http请求_第2张图片

 

1、创建线程,从线程池中获取空闲的线程,如果没有则创建,等线程池那一套, task.run();

2、ServerImpl的一个内部类Exchange实现了Runnable接口,在run方法里面执行部分逻辑;Exchange有一下几个重要的属性:

SocketChannel chan;

HttpConnection connection;

HttpContextImpl context;

InputStream rawin;

OutputStream rawout;

String protocol;

ExchangeImpl tx;

HttpContextImpl ctx;

boolean rejected = false;

大致逻辑如下

1、首先获取上下文对象,判断上下文是否为空,如果context不是空,则初始化 rawin和rawout,从connection对象的输入输出流中获取;

2、如果上下文是空的,则在SocketChannel中获取数据,初始化rawin和rawout;

3、根据获取的rawin和rawout创建一个Request对象,去判断Request对象的requestLine是否为空,如果是空的,则关闭当前链接,直接返回;

4、如果requestLine不是空,则去校验requestLine的合法性,如果requestList不合法,直接抛400异常Bad request line;

5、如果请求是合法的,然后根据请求的uri中的path,寻找是否存在context,参数是 this.protocol, var10.getPath(),如果没有找到path,则抛404异常,核心代码如下:

this.ctx = ServerImpl.this.contexts.findContext(this.protocol, var10.getPath());

if (this.ctx == null) {

    this.reject(404, var3, "No context found for request");

    return;

}

6、找到HttpContextImpl对象之后,还要校验context是否设置了handler,如果没有配置handler,则抛500错误;

this.connection.setContext(this.ctx);

if (this.ctx.getHandler() == null) {

    this.reject(500, var3, "No handler for context");

    return;

}

7、然后设置请求的Header和Parameters,初始化ExchangeImpl对象;

8、之后构造Chain对象,执行doFilter方法,doFilter方法的核心就是调用响应的handler方法的逻辑,核心代码如下:

List var28 = this.ctx.getSystemFilters();

List var29 = this.ctx.getFilters();

Chain var21 = new Chain(var28, this.ctx.getHandler());

Chain var22 = new Chain(var29, new ServerImpl.Exchange.LinkHandler(var21));

 

 

//  Chain类如下

@Exported

public static class Chain {

    private ListIterator iter;

    private HttpHandler handler;

 

    public Chain(List var1, HttpHandler var2) {

        this.iter = var1.listIterator();

        this.handler = var2;

    }

 

    public void doFilter(HttpExchange var1) throws IOException {

        if (!this.iter.hasNext()) {

            // 执行handle方法,也就是我们实现Handle接口,重写的handle方法

            this.handler.handle(var1);

        else {

            Filter var2 = (Filter)this.iter.next();

            var2.doFilter(var1, this);

        }

 

    }

}

9、如果调用hander方法发生异常时,则调用reject方法,reject方法主要操作氛围两部分,第一是打印错误日志,第二是关闭当前连接,关闭当前连接的操作实质是将存放HttpConnection的Set中remove当前连接对象。

你可能感兴趣的:(Java)