功能完整的HTTP服务器

导语

这个一个功能完备的HTTP服务器。它可以提供一个完整的文档输,包括图像,applet,HTML文件,文本文件。它与SingleFileHttpServer非常相似,只不过它所关注的是GET请求的内容。它会根据GET请求的内容在自己的工作目录查找对应的资源,并将该资源返回给用户。这个服务是相当轻量级的。

主线程代码

import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger; public class JHTTP {
//开启日志
private static final Logger logger = Logger.getLogger(JHTTP.class.getCanonicalName());
//线程数
private static final int NUM_THREAD = 50;
//默认主页
private static final String INDEX_FILE = "index.html";
//服务器工作目录
private final File rootDirectory;
//端口号
private final int port; /**
*
* @param _rootDirectory 工作目录
* @param _port 端口号
*/
public JHTTP(File _rootDirectory, int _port) {
if (!_rootDirectory.isDirectory())
throw new RuntimeException(_rootDirectory + "does not exist as a directory");
rootDirectory = _rootDirectory;
port = _port;
} /**
* 启动服务器
* @throws IOException
*/
public void start() throws IOException {
ExecutorService pool = Executors.newFixedThreadPool(NUM_THREAD);
try (ServerSocket server = new ServerSocket(port)) {
logger.info("Accepting connection on port" + server.getLocalPort());
logger.info("Document Root: " + rootDirectory);
while (true) {
try {
Socket request = server.accept();
pool.execute(new RequestProcessor(rootDirectory, INDEX_FILE, request));
} catch (IOException e) {
logger.warning("Error accepting connection");
}
}
}
} public static void main(String[] args) { //设置工作目录
File docroot;
try {
docroot = new File(args[0]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("Usage: java JHTTP docroot port");
return;
} //设置监听端口号
int port;
try {
port = Integer.parseInt(args[1]);
if (port < 0 || port > 65535) port = 8080;
} catch (RuntimeException e) {
port = 8080;
} try {
JHTTP webserver = new JHTTP(docroot, port);
webserver.start();
} catch (IOException e) {
logger.severe("Server cloud not start");
}
}
}

主线程代码比较简单,默认监听8080端口,将连接提交给工作线程来处理。

处理线程

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.Socket;
import java.net.URLConnection;
import java.nio.file.Files;
import java.util.Date;
import java.util.logging.Logger; public class RequestProcessor implements Runnable { private final static Logger logger = Logger.getLogger(RequestProcessor.class.getCanonicalName());
private File rootDirectory;
private String indexFileName = "index.html";
private Socket conn; public RequestProcessor(File _rootDirectory, String _indexFileName,
Socket _conn) {
if (_rootDirectory.isFile())
throw new IllegalArgumentException("rootDirectory muse be a directory, not a file");
rootDirectory = _rootDirectory;
indexFileName = _indexFileName;
conn = _conn;
} @Override
public void run() {
String root = rootDirectory.getPath();
try {
BufferedOutputStream raw = new BufferedOutputStream(conn.getOutputStream());
Writer out = new BufferedWriter(new OutputStreamWriter(raw, "utf-8"));
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String get = in.readLine();
if (get != null) {
logger.info(conn.getRemoteSocketAddress() + " " + get);
String[] pieces = get.split("\\s+");
String method = pieces[0];
String version = "";
if (method.equals("GET")) {
String fileName = pieces[1];
if (fileName.endsWith("/")) fileName += indexFileName;
String contentType = URLConnection.getFileNameMap().getContentTypeFor(root +fileName);
if (pieces.length > 2) {
version = pieces[2];
}
File theFile = new File(rootDirectory, fileName.substring(1, fileName.length()));
if (theFile.canRead() && theFile.getCanonicalPath().startsWith(root)) {
byte[] theData = Files.readAllBytes(theFile.toPath());
if (version.startsWith("HTTP/"))
sendHeader(out, "HTTP/1.1 200 OK", contentType, theData.length);
raw.write(theData);
raw.flush();
raw.close();
} else { //无法找到文件
String body = "<html><head><title>File not found</title></head><body>Error 404:文件未找到</body></html>";
if (version.startsWith("HTTP/"))
sendHeader(out, "HTTP/1.1 4O4 File Not Found", "text/html;charset=utf-8", body.getBytes("utf-8").length);
out.write(body);
out.flush();
out.close();
}
} else {
String body = "<html><head><title>File not found</title></head><body>Error 501:无法处理该请求</body></html>";
if (version.startsWith("HTTP/"))
sendHeader(out, "HTTP/1.1 5O1 Not Implemented", "text/html;charset=utf-8", body.getBytes("utf-8").length);
out.write(body);
out.flush();
out.close();
}
}
} catch (IOException e){
logger.warning("Error talking to " + conn.getRemoteSocketAddress());
} finally {
try {
conn.close();
} catch (IOException e) {}
}
} private void sendHeader(Writer out, String responseCode, String contentType, int length) throws IOException {
out.write(responseCode + "\r\n");
out.write("Date: " + new Date() + "\r\n");
out.write("Server: JHTTP 2.0\r\n");
out.write("Content-Type: " + contentType + "\r\n");
out.write("Content-Length: " + length + "\r\n\r\n");
out.flush();
}
}

在处理线程中处理客户端的请求,通过解析GET请求的资源从本地中查找对应的资源。如果没有找的则返回404错误