NanoHTTPD源码剖析

NanoHttpd是一个用Java写的HTTP服务器,堪称蝇级,但麻雀虽小,五脏俱全,在GitHub上有着两千多个Star,甚为可观。

我之前有读过好几个Java的HTTP Server的源码,有TomcatJettySpark(一个内嵌了Jetty的RESTful Framework),Undertow(JBoss出的一个轻量级Web Server),Vert.x等等,但它们的代码量都不小,同时还糅合了各种设计模式,很容易就迷失在里面各种细节当中。

这个NanoHttpd相比之下就简单得多,正如它的名字所说的,纳米级的HTTP Daemon,只需要对Java Socket API熟悉一点,花个一小时左右就能把它前前后后的细节都捋清楚,不失为一个很好的入门级HTTP Server。

我们先从它的一个HelloWorld Demo入手: 运行后,打开浏览器输入http://localhost:8080, 就可以看到以下页面:
然后输入一个名字后,就会看到:

上面例子实现的功能非常简单,就是接收一个输入,然后展现在页面。我们从代码中可以看到, HelloServer继承了org.nanohttpd.protocols.http.NanoHTTPD这个类,然后通过org.nanohttpd.util.ServerRunner将HelloServer跑起来。 那我们进NanoHTTPD里面看它到底是一个什么东西。

首先NanoHTTPD是一个抽象类:
public abstract class NanoHTTPD
我们再看一下它的Java Doc里面说的: 就是说你的类只需要继承它并实现serve()方法就可以实现一个嵌入式的HTTP Server。 接着看一下它的Constructor: 里面的hostname和port不必多说,这里要注意的是设置了一个AsyncRunnerhttpHandler,从它们的名字我们就大概能知道它们是干嘛的,AsyncRunner,肯定是用来异步接收和处理网络请求的,而httpHandler自然是用来处理请求和发送响应的。

我们进DefaultAsyncRunner看看: 从它的Java Doc可以看到,NanoHTTPD为每个网络请求都创建了一条线程来处理,这也是NanoHTTPD为何如此简单和它的局限所在:

再看一下httpHandler,它是一个实现了IHandler接口的匿名类: 注意这里,NanoHTTPD.this.serve(input)其实就是调用了继承了NanoHTTPD的子类的serve方法。

接下来就是看ServerRunner是如何执行的了。 从上面代码可以看到,它是通过反射new出一个NanoHTTPD的子类,然后传给executeInstance方法:

接着就是到NanoHTTPDstart方法了,这个方法就是NanoHTTP的核心:

它先通过工厂类创建了一个ServerSocket出来, 然后创建一个ServerRunnable对象,然后根据这个ServerRunnable对象新建一个线程并启动该线程,该线程就是负责监听所有网络请求的主线程。ServerRunnable类实现了Runnable接口,它的run方法如下: 从以上代码可以看到,ServerRunnable其实就是将前面通过工厂类创建出来的ServerSocket跟hostname和port绑定,然后调用阻塞的accpet方法,持续监听网络请求,当有网络请求过来时,就将该请求的socket和该socket的inputstream组装成一个ClientHandler,传给AsyncRunner来进行异步处理。ClientHandler类实现了Runnable接口,它的run方法如下:

ClientHandler会把socket跟它的InputStream和OutputStream组装成一个HTTPSession类,然后调用它的execute方法。execute方法里先是通过inputstream读取出网络请求中的各种HTTP字段,然后再调用NanoHTTPD的handle方法: NanoHTTPD的handle方法:
如果有设置了拦截器,该请求就会被拦截器拦截到,然后被拦截器处理掉,否则就会被httpHandler处理,调用该httpHandler的handle方法,这时就该回到最前面赋给NanoHTTPD的httpHandler的那个实现了IHandler接口的匿名类:

而该匿名类的handle方法实际上调用的就是继承了NanoHTTPD的子类对象的serve方法,也就是本例中的HelloServerserve方法!

最后,再回到HTTPSession的run方法里面,调用Response的send方法,将响应发送给客户端: Response的send方法如下:

至此,NanoHTTPD从启动server,到接收网络请求,处理网络请求,发送响应,整个过程源码剖析完毕。其中处理网络请求的细节还值得详细剖析一遍,就可以清晰地看到HTTP的结构,我会另起一篇文章来分析。

Written on 16 May 2017