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请求
文章图片

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) {
【JDK自带HttpServer处理Http请求】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请求
文章图片


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当前连接对象。

    推荐阅读