下面重点介绍上篇文章介绍的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. win10清理C盘

    用win10系统也不是很久,发现C盘容量越来越小了,于是乎扫描了一下,发现有两个文件容量很大 C:/hiberfil.syssize:1660444672 C:/swapfile.syssize:26 ...

  2. Java的RandomAccessFile

    Java的RandomAccessFile提供对文件的读写功能,与普通的输入输出流不一样的是RamdomAccessFile可以任意的访问文件的任何地方.这就是“Random”的意义所在. Rando ...

  3. extjs_02_grid(显示本地数据,显示跨域数据)

    1.显示表格 <%@ page language="java" import="java.util.*" pageEncoding="UTF-8 ...

  4. Chapter 5 - How to Detect the Collisions

    Chapter 5 - How to Detect the Collisions Our hero can fire bullets now, but the bullets are only vis ...

  5. Android 上使用 iconfont 的一种便捷方案

    最近在学习 AIOSO(Alibaba Internal Open Source Organization,即阿里巴巴内部开源组织) 的一个子项目MMCherryUI,这是一个流式布局,可以在运行时做 ...

  6. android自定义控件之滚动广告条

    在一些电子商务网站上经常能够看到一些滚动的广告条,许多软件在首次使用时也有类似的广告条,如图: 其实在github上有实现这种效果的控件,不过这东西做起来也是很简单,我们今天就来看看该怎么做. 先来看 ...

  7. VC++文件相关操作的函数封装实现

    在开发编译工具中,需要用到文件的相关操作,于是就封装了相关的函数实现: //判断文件是否存在 BOOL FileIsExist(CString strFileName) { CFileFind fin ...

  8. Java代码操作HDFS(在/user/root/下面創建目錄)

    1.创建HDFS目录并打成jar包 package Hdfs; import java.io.IOException; import java.net.URI; import org.apache.h ...

  9. C# 的可空合并运算符(??)到底是怎样的宝宝?

    前言废语 也怪自己小白和不勤奋,没有系统的学习C#相关的东西,工作一年多还是初级小菜,深感不安,来到园子才发现好多钻研技术的人,也渐渐发现自己开始喜欢上了这个编程的世界.今日偶遇??操作符,发现我只看 ...

  10. ASP.Net IE10+ SCRIPT:XXX_doPostBack 未定义

    问题描述 GridView中分页控件,点击分页无反应,Linkbutton点击无反应,打开Web控制台,发现如下错误:SCRIPTXXX:_doPostBack 未定义:查询后得知,是由于.NET F ...