下面重点介绍上篇文章介绍的HttpServerImpl类当中的ServerImpl类

sun.net.httpserver.ServerImpl   600行左右的类,是整个HttpServer的核心类

无论是一般的HTTP请求还是加入SSL层的HTTPS请求都在这里处理

package sun.net.httpserver;

import java.net.*;
import java.io.*;
import java.nio.*;
import java.security.*;
import java.nio.channels.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.logging.Logger;
import java.util.logging.Level;
import javax.net.ssl.*;
import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*; /**
* Provides implementation for both HTTP and HTTPS
*/
class ServerImpl implements TimeSource { private String protocol;
private boolean https;
private Executor executor;
private HttpsConfigurator httpsConfig;
private SSLContext sslContext;
private ContextList contexts;
private InetSocketAddress address;
private ServerSocketChannel schan;
private Selector selector;
private SelectionKey listenerKey;
private Set<HttpConnection> idleConnections;
private Set<HttpConnection> allConnections;
private List<Event> events;
private Object lolock = new Object();
private volatile boolean finished = false;
private volatile boolean terminating = false;
private boolean bound = false;
private boolean started = false;
private volatile long time; /* current time */
private volatile long ticks; /* number of clock ticks since server started */
private HttpServer wrapper; final static int CLOCK_TICK = ServerConfig.getClockTick();
final static long IDLE_INTERVAL = ServerConfig.getIdleInterval();
final static int MAX_IDLE_CONNECTIONS = ServerConfig.getMaxIdleConnections(); private Timer timer;
private Logger logger; ServerImpl (
HttpServer wrapper, String protocol, InetSocketAddress addr, int backlog
) throws IOException { this.protocol = protocol;
this.wrapper = wrapper;
this.logger = Logger.getLogger ("com.sun.net.httpserver");
https = protocol.equalsIgnoreCase ("https");
this.address = addr;
contexts = new ContextList();
schan = ServerSocketChannel.open();
if (addr != null) {
ServerSocket socket = schan.socket();
socket.bind (addr, backlog);
bound = true;
}
selector = Selector.open ();
schan.configureBlocking (false);
listenerKey = schan.register (selector, SelectionKey.OP_ACCEPT);
dispatcher = new Dispatcher();
idleConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
allConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
time = System.currentTimeMillis();
timer = new Timer ("server-timer", true);
timer.schedule (new ServerTimerTask(), CLOCK_TICK, CLOCK_TICK);
events = new LinkedList<Event>();
logger.config ("HttpServer created "+protocol+" "+ addr);
} public void bind (InetSocketAddress addr, int backlog) throws IOException {
if (bound) {
throw new BindException ("HttpServer already bound");
}
if (addr == null) {
throw new NullPointerException ("null address");
}
ServerSocket socket = schan.socket();
socket.bind (addr, backlog);
bound = true;
} public void start () {
if (!bound || started || finished) {
throw new IllegalStateException ("server in wrong state");
}
if (executor == null) {
executor = new DefaultExecutor();
}
Thread t = new Thread (dispatcher);
started = true;
t.start();
} public void setExecutor (Executor executor) {
if (started) {
throw new IllegalStateException ("server already started");
}
this.executor = executor;
} private static class DefaultExecutor implements Executor {
public void execute (Runnable task) {
task.run();
}
} public Executor getExecutor () {
return executor;
} public void setHttpsConfigurator (HttpsConfigurator config) {
if (config == null) {
throw new NullPointerException ("null HttpsConfigurator");
}
if (started) {
throw new IllegalStateException ("server already started");
}
this.httpsConfig = config;
sslContext = config.getSSLContext();
} public HttpsConfigurator getHttpsConfigurator () {
return httpsConfig;
} public void stop (int delay) {
if (delay < 0) {
throw new IllegalArgumentException ("negative delay parameter");
}
terminating = true;
try { schan.close(); } catch (IOException e) {}
selector.wakeup();
long latest = System.currentTimeMillis() + delay * 1000;
while (System.currentTimeMillis() < latest) {
delay();
if (finished) {
break;
}
}
finished = true;
selector.wakeup();
synchronized (allConnections) {
for (HttpConnection c : allConnections) {
c.close();
}
}
allConnections.clear();
idleConnections.clear();
timer.cancel();
} Dispatcher dispatcher; public synchronized HttpContextImpl createContext (String path, HttpHandler handler) {
if (handler == null || path == null) {
throw new NullPointerException ("null handler, or path parameter");
}
HttpContextImpl context = new HttpContextImpl (protocol, path, handler, this);
contexts.add (context);
logger.config ("context created: " + path);
return context;
} public synchronized HttpContextImpl createContext (String path) {
if (path == null) {
throw new NullPointerException ("null path parameter");
}
HttpContextImpl context = new HttpContextImpl (protocol, path, null, this);
contexts.add (context);
logger.config ("context created: " + path);
return context;
} public synchronized void removeContext (String path) throws IllegalArgumentException {
if (path == null) {
throw new NullPointerException ("null path parameter");
}
contexts.remove (protocol, path);
logger.config ("context removed: " + path);
} public synchronized void removeContext (HttpContext context) throws IllegalArgumentException {
if (!(context instanceof HttpContextImpl)) {
throw new IllegalArgumentException ("wrong HttpContext type");
}
contexts.remove ((HttpContextImpl)context);
logger.config ("context removed: " + context.getPath());
} public InetSocketAddress getAddress() {
return (InetSocketAddress)schan.socket().getLocalSocketAddress();
} Selector getSelector () {
return selector;
} void addEvent (Event r) {
synchronized (lolock) {
events.add (r);
selector.wakeup();
}
} int resultSize () {
synchronized (lolock) {
return events.size ();
}
} /* main server listener task */ class Dispatcher implements Runnable { private void handleEvent (Event r) {
ExchangeImpl t = r.exchange;
HttpConnection c = t.getConnection();
try {
if (r instanceof WriteFinishedEvent) { int exchanges = endExchange();
if (terminating && exchanges == 0) {
finished = true;
}
SocketChannel chan = c.getChannel();
LeftOverInputStream is = t.getOriginalInputStream();
if (!is.isEOF()) {
t.close = true;
}
if (t.close || idleConnections.size() >= MAX_IDLE_CONNECTIONS) {
c.close();
allConnections.remove (c);
} else {
if (is.isDataBuffered()) {
/* don't re-enable the interestops, just handle it */
handle (c.getChannel(), c);
} else {
/* re-enable interestops */
SelectionKey key = c.getSelectionKey();
if (key.isValid()) {
key.interestOps (
key.interestOps()|SelectionKey.OP_READ
);
}
c.time = getTime() + IDLE_INTERVAL;
idleConnections.add (c);
}
}
}
} catch (IOException e) {
logger.log (
Level.FINER, "Dispatcher (1)", e
);
c.close();
}
} public void run() {
while (!finished) {
try { /* process the events list first */ while (resultSize() > 0) {
Event r;
synchronized (lolock) {
r = events.remove(0);
handleEvent (r);
}
} selector.select(1000); /* process the selected list now */ Set<SelectionKey> selected = selector.selectedKeys();
Iterator<SelectionKey> iter = selected.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
iter.remove ();
if (key.equals (listenerKey)) {
if (terminating) {
continue;
}
SocketChannel chan = schan.accept();
if (chan == null) {
continue; /* cancel something ? */
}
chan.configureBlocking (false);
SelectionKey newkey = chan.register (selector, SelectionKey.OP_READ);
HttpConnection c = new HttpConnection ();
c.selectionKey = newkey;
c.setChannel (chan);
newkey.attach (c);
allConnections.add (c);
} else {
try {
if (key.isReadable()) {
boolean closed;
SocketChannel chan = (SocketChannel)key.channel();
HttpConnection conn = (HttpConnection)key.attachment();
// interestOps will be restored at end of read
key.interestOps (0);
handle (chan, conn);
} else {
assert false;
}
} catch (IOException e) {
HttpConnection conn = (HttpConnection)key.attachment();
logger.log (
Level.FINER, "Dispatcher (2)", e
);
conn.close();
}
}
}
} catch (CancelledKeyException e) {
logger.log (Level.FINER, "Dispatcher (3)", e);
} catch (IOException e) {
logger.log (Level.FINER, "Dispatcher (4)", e);
} catch (Exception e) {
logger.log (Level.FINER, "Dispatcher (7)", e);
}
}
} public void handle (SocketChannel chan, HttpConnection conn)
throws IOException
{
try {
Exchange t = new Exchange (chan, protocol, conn);
executor.execute (t);
} catch (HttpError e1) {
logger.log (Level.FINER, "Dispatcher (5)", e1);
conn.close();
} catch (IOException e) {
logger.log (Level.FINER, "Dispatcher (6)", e);
conn.close();
}
}
} static boolean debug = ServerConfig.debugEnabled (); static synchronized void dprint (String s) {
if (debug) {
System.out.println (s);
}
} static synchronized void dprint (Exception e) {
if (debug) {
System.out.println (e);
e.printStackTrace();
}
} Logger getLogger () {
return logger;
} /* per exchange task */ class Exchange implements Runnable {
SocketChannel chan;
HttpConnection connection;
HttpContextImpl context;
InputStream rawin;
OutputStream rawout;
String protocol;
ExchangeImpl tx;
HttpContextImpl ctx;
boolean rejected = false; Exchange (SocketChannel chan, String protocol, HttpConnection conn) throws IOException {
this.chan = chan;
this.connection = conn;
this.protocol = protocol;
} public void run () {
/* context will be null for new connections */
context = connection.getHttpContext();
boolean newconnection;
SSLEngine engine = null;
String requestLine = null;
SSLStreams sslStreams = null;
try {
if (context != null ) {
this.rawin = connection.getInputStream();
this.rawout = connection.getRawOutputStream();
newconnection = false;
} else {
/* figure out what kind of connection this is */
newconnection = true;
if (https) {
if (sslContext == null) {
logger.warning ("SSL connection received. No https contxt created");
throw new HttpError ("No SSL context established");
}
sslStreams = new SSLStreams (ServerImpl.this, sslContext, chan);
rawin = sslStreams.getInputStream();
rawout = sslStreams.getOutputStream();
engine = sslStreams.getSSLEngine();
connection.sslStreams = sslStreams;
} else {
rawin = new BufferedInputStream(
new Request.ReadStream (
ServerImpl.this, chan
));
rawout = new Request.WriteStream (
ServerImpl.this, chan
);
}
connection.raw = rawin;
connection.rawout = rawout;
}
Request req = new Request (rawin, rawout);
requestLine = req.requestLine();
if (requestLine == null) {
/* connection closed */
connection.close();
return;
}
int space = requestLine.indexOf (' ');
if (space == -1) {
reject (Code.HTTP_BAD_REQUEST,
requestLine, "Bad request line");
return;
}
String method = requestLine.substring (0, space);
int start = space+1;
space = requestLine.indexOf(' ', start);
if (space == -1) {
reject (Code.HTTP_BAD_REQUEST,
requestLine, "Bad request line");
return;
}
String uriStr = requestLine.substring (start, space);
URI uri = new URI (uriStr);
start = space+1;
String version = requestLine.substring (start);
Headers headers = req.headers();
String s = headers.getFirst ("Transfer-encoding");
int clen = 0;
if (s !=null && s.equalsIgnoreCase ("chunked")) {
clen = -1;
} else {
s = headers.getFirst ("Content-Length");
if (s != null) {
clen = Integer.parseInt (s);
}
}
ctx = contexts.findContext (protocol, uri.getPath());
if (ctx == null) {
reject (Code.HTTP_NOT_FOUND,
requestLine, "No context found for request");
return;
}
connection.setContext (ctx);
if (ctx.getHandler() == null) {
reject (Code.HTTP_INTERNAL_ERROR,
requestLine, "No handler for context");
return;
}
tx = new ExchangeImpl (
method, uri, req, clen, connection
);
String chdr = headers.getFirst("Connection");
Headers rheaders = tx.getResponseHeaders(); if (chdr != null && chdr.equalsIgnoreCase ("close")) {
tx.close = true;
}
if (version.equalsIgnoreCase ("http/1.0")) {
tx.http10 = true;
if (chdr == null) {
tx.close = true;
rheaders.set ("Connection", "close");
} else if (chdr.equalsIgnoreCase ("keep-alive")) {
rheaders.set ("Connection", "keep-alive");
int idle=(int)ServerConfig.getIdleInterval()/1000;
int max=(int)ServerConfig.getMaxIdleConnections();
String val = "timeout="+idle+", max="+max;
rheaders.set ("Keep-Alive", val);
}
} if (newconnection) {
connection.setParameters (
rawin, rawout, chan, engine, sslStreams,
sslContext, protocol, ctx, rawin
);
}
/* check if client sent an Expect 100 Continue.
* In that case, need to send an interim response.
* In future API may be modified to allow app to
* be involved in this process.
*/
String exp = headers.getFirst("Expect");
if (exp != null && exp.equalsIgnoreCase ("100-continue")) {
logReply (100, requestLine, null);
sendReply (
Code.HTTP_CONTINUE, false, null
);
}
/* uf is the list of filters seen/set by the user.
* sf is the list of filters established internally
* and which are not visible to the user. uc and sc
* are the corresponding Filter.Chains.
* They are linked together by a LinkHandler
* so that they can both be invoked in one call.
*/
List<Filter> sf = ctx.getSystemFilters();
List<Filter> uf = ctx.getFilters(); Filter.Chain sc = new Filter.Chain(sf, ctx.getHandler());
Filter.Chain uc = new Filter.Chain(uf, new LinkHandler (sc)); /* set up the two stream references */
tx.getRequestBody();
tx.getResponseBody();
if (https) {
uc.doFilter (new HttpsExchangeImpl (tx));
} else {
uc.doFilter (new HttpExchangeImpl (tx));
} } catch (IOException e1) {
logger.log (Level.FINER, "ServerImpl.Exchange (1)", e1);
connection.close();
} catch (NumberFormatException e3) {
reject (Code.HTTP_BAD_REQUEST,
requestLine, "NumberFormatException thrown");
} catch (URISyntaxException e) {
reject (Code.HTTP_BAD_REQUEST,
requestLine, "URISyntaxException thrown");
} catch (Exception e4) {
logger.log (Level.FINER, "ServerImpl.Exchange (2)", e4);
connection.close();
}
} /* used to link to 2 or more Filter.Chains together */ class LinkHandler implements HttpHandler {
Filter.Chain nextChain; LinkHandler (Filter.Chain nextChain) {
this.nextChain = nextChain;
} public void handle (HttpExchange exchange) throws IOException {
nextChain.doFilter (exchange);
}
} void reject (int code, String requestStr, String message) {
rejected = true;
logReply (code, requestStr, message);
sendReply (
code, true, "<h1>"+code+Code.msg(code)+"</h1>"+message
);
} void sendReply (
int code, boolean closeNow, String text)
{
try {
String s = "HTTP/1.1 " + code + Code.msg(code) + "\r\n";
if (text != null && text.length() != 0) {
s = s + "Content-Length: "+text.length()+"\r\n";
s = s + "Content-Type: text/html\r\n";
} else {
s = s + "Content-Length: 0\r\n";
text = "";
}
if (closeNow) {
s = s + "Connection: close\r\n";
}
s = s + "\r\n" + text;
byte[] b = s.getBytes("ISO8859_1");
rawout.write (b);
rawout.flush();
if (closeNow) {
connection.close();
}
} catch (IOException e) {
logger.log (Level.FINER, "ServerImpl.sendReply", e);
connection.close();
}
} } void logReply (int code, String requestStr, String text) {
if (text == null) {
text = "";
}
String message = requestStr + " [" + code + " " +
Code.msg(code) + "] ("+text+")";
logger.fine (message);
} long getTicks() {
return ticks;
} public long getTime() {
return time;
} void delay () {
Thread.yield();
try {
Thread.sleep (200);
} catch (InterruptedException e) {}
} private int exchangeCount = 0; synchronized void startExchange () {
exchangeCount ++;
} synchronized int endExchange () {
exchangeCount --;
assert exchangeCount >= 0;
return exchangeCount;
} HttpServer getWrapper () {
return wrapper;
} /**
* TimerTask run every CLOCK_TICK ms
*/
class ServerTimerTask extends TimerTask {
public void run () {
LinkedList<HttpConnection> toClose = new LinkedList<HttpConnection>();
time = System.currentTimeMillis();
ticks ++;
synchronized (idleConnections) {
for (HttpConnection c : idleConnections) {
if (c.time <= time) {
toClose.add (c);
}
}
for (HttpConnection c : toClose) {
idleConnections.remove (c);
allConnections.remove (c);
c.close();
}
}
}
}
}

  

Java实现Http服务器(三)的更多相关文章

  1. 一个java页游服务器框架

    一.前言 此游戏服务器架构是一个单服的形式,也就是说所有游戏逻辑在一个工程里,没有区分登陆服务器.战斗服务器.世界服务器等.此架构已成功应用在了多款页游服务器 .在此框架中没有实现相关业务逻辑,只有简 ...

  2. JAVA编写WEB服务器

    一.超文本传输协议  1.1 HTTP请求  1.2 HTTP应答  二.Socket类  三.ServerSocket类  四.Web服务器实例  4.1 HttpServer类  4.2 Requ ...

  3. 【JAVA编码专题】 JAVA字符编码系列三:Java应用中的编码问题

    这两天抽时间又总结/整理了一下各种编码的实际编码方式,和在Java应用中的使用情况,在这里记录下来以便日后参考. 为了构成一个完整的对文字编码的认识和深入把握,以便处理在Java开发过程中遇到的各种问 ...

  4. 实战WEB 服务器(JAVA编写WEB服务器)

    实战WEB 服务器(JAVA编写WEB服务器) 标签: web服务服务器javawebsockethttp服务器 2010-04-21 17:09 11631人阅读 评论(24) 收藏 举报  分类: ...

  5. [翻译]现代java开发指南 第三部分

    现代java开发指南 第三部分 第三部分:Web开发 第一部分,第二部分,第三部分 =========================== 欢迎来到现代 Java 开发指南第三部分.在第一部分中,我们 ...

  6. JAVA之旅(三十三)——TCP传输,互相(伤害)传输,复制文件,上传图片,多并发上传,多并发登录

    JAVA之旅(三十三)--TCP传输,互相(伤害)传输,复制文件,上传图片,多并发上传,多并发登录 我们继续网络编程 一.TCP 说完UDP,我们就来说下我们应该重点掌握的TCP了 TCP传输 Soc ...

  7. Java虚拟机垃圾回收(三) 7种垃圾收集器

    Java虚拟机垃圾回收(三) 7种垃圾收集器 主要特点 应用场景 设置参数 基本运行原理 在<Java虚拟机垃圾回收(一) 基础>中了解到如何判断对象是存活还是已经死亡?在<Java ...

  8. java 日志体系(三)log4j从入门到详解

    java 日志体系(三)log4j从入门到详解 一.Log4j 简介 在应用程序中添加日志记录总的来说基于三个目的: 监视代码中变量的变化情况,周期性的记录到文件中供其他应用进行统计分析工作: 跟踪代 ...

  9. 一个JAVA的WEB服务器事例

    其实编写一个入门级别的JAVA的WEB服务器,很简单,用SOCKET类即可实现.相关内容可以参考:http://www.cnblogs.com/liqiu/p/3253022.html 一.首先创建一 ...

随机推荐

  1. JavaScript-每隔5分钟执行一次ajax请求的实现方法

    个页面好像只能有一个 window.onload=function(){},所以要有多个事件,这样写就好了 window.onload=function(){ //假设这里每个五分钟执行一次test函 ...

  2. CentOS系统下做nginx和tomcat负载均衡

    系统总是频繁更新,为了避免更新系统的时候领导看不到东西,打算用ngix做代理,后台部署两个tomcat做负载均衡,避免更新一台就无法使用系统的问题,这两天看了写资料,把几个关键点记录在这里以便备忘. ...

  3. xmemcached user guide --存档

    XMemcached Introduction XMemcached is a new java memcached client. Maybe you don't know "memcac ...

  4. react native web

    http://rawgit.com/taobaofed/react-web/master/pages/uiexplorer.html#/scene_1?_k=7vm99j

  5. Linux下搭建Oracle11g RAC(1)----IP分配与配置IP

    首先需要说明的,我的RAC搭建不是在虚拟机上完成的,而是在实际部署中,二者之间有些许差异,本人水平有限,请见谅. 其中,每台机器至少需要配置3个IP地址,在安装操作系统的过程中,我们需要配置公网IP和 ...

  6. replace()、replaceFirst()和replaceAll()的区别

    1.replace() String str= "mesquite in your cellar" str.replace('e', 'o') returns "mosq ...

  7. 各种SQL类型

    一.SQL:通常我们说的sql指的是最古老运用最方法的结构化查询语言(Structured Query Language),大部分人接触最多的是数据库查询使用,关系型数据库基本都支持. 二.T-SQL ...

  8. MongoDB的安装和基本操作

    一.使用前的准备(windows下的安装)  1.下载 目前MongoDB的官网不知道问什么不能进行下载了,但是可以在MongoDB中文论坛进行下载, 地址如下:http://www.mongoing ...

  9. NodeJS学习笔记—2.AMD规范

    CommonJS加载模块是同步的,而AMD模块加在是非同步的,允许指定回调函数.由于Nodejs主要用于服务器编程,模块文件一般都存在于本地,所以加载很快,不需要考虑非同步加载,用CommonJS即可 ...

  10. Powershell profile.ps1 cannot be loaded because its operation is blocked by software restriction policies

    Powershell profile.ps1 cannot be loaded because its operation is blocked by software restriction pol ...