Java 如何使用suns simple httpserver提供静态内容
我正在使用jersey的Java 如何使用suns simple httpserver提供静态内容,java,jersey,httpserver,Java,Jersey,Httpserver,我正在使用jersey的HttpServerFactory创建一个简单的嵌入式HttpServer,它承载了几个rest服务。我们只需要一些小巧、快速、轻便的东西。我需要在同一个服务器实例中托管一个小的静态html页面。有没有简单的方法向服务器添加静态处理程序?是否有预定义的处理程序可以使用?这似乎是一项非常常见的任务,如果它已经存在,我不愿意为它重新编写代码 server = HttpServerFactory.create(url); server.setExecutor(Executors
HttpServerFactory
创建一个简单的嵌入式HttpServer
,它承载了几个rest服务。我们只需要一些小巧、快速、轻便的东西。我需要在同一个服务器实例中托管一个小的静态html页面。有没有简单的方法向服务器添加静态处理程序?是否有预定义的处理程序可以使用?这似乎是一项非常常见的任务,如果它已经存在,我不愿意为它重新编写代码
server = HttpServerFactory.create(url);
server.setExecutor(Executors.newCachedThreadPool());
server.createContext("/staticcontent", new HttpHandler() {
@Override
public void handle(HttpExchange arg0) throws IOException {
//What goes here?
}
});
server.start();
这将达到目的,尽管它允许任何人通过请求..// 您可以将./wwwroot更改为任何有效的java文件路径
static class MyHandler implements HttpHandler {
public void handle(HttpExchange t) throws IOException {
String root = "./wwwroot";
URI uri = t.getRequestURI();
System.out.println("looking for: "+ root + uri.getPath());
String path = uri.getPath();
File file = new File(root + path).getCanonicalFile();
if (!file.isFile()) {
// Object does not exist or is not a file: reject with 404 error.
String response = "404 (Not Found)\n";
t.sendResponseHeaders(404, response.length());
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
} else {
// Object exists and is a file: accept with response code 200.
String mime = "text/html";
if(path.substring(path.length()-3).equals(".js")) mime = "application/javascript";
if(path.substring(path.length()-3).equals("css")) mime = "text/css";
Headers h = t.getResponseHeaders();
h.set("Content-Type", mime);
t.sendResponseHeaders(200, 0);
OutputStream os = t.getResponseBody();
FileInputStream fs = new FileInputStream(file);
final byte[] buffer = new byte[0x10000];
int count = 0;
while ((count = fs.read(buffer)) >= 0) {
os.write(buffer,0,count);
}
fs.close();
os.close();
}
}
}
这是一个安全的版本。您可能需要添加几个MIME类型,具体取决于哪些类型是常见的(如果需要,也可以使用另一种方法)
package de.phihag.miniticker;
导入java.io.File;
导入java.io.FileInputStream;
导入java.io.FileNotFoundException;
导入java.io.IOException;
导入java.io.InputStream;
导入java.io.OutputStream;
导入java.util.HashMap;
导入java.util.Map;
导入com.sun.net.httpserver.HttpExchange;
导入com.sun.net.httpserver.HttpHandler;
导入com.sun.net.httpserver.httpserver;
公共类StaticFileHandler实现HttpHandler{
私有静态最终映射MIME_Map=new HashMap();
静止的{
MIME_MAP.put(“appcache”、“文本/缓存清单”);
MIME_MAP.put(“css”、“text/css”);
MIME_MAP.put(“gif”、“image/gif”);
MIME_MAP.put(“html”、“text/html”);
MIME_MAP.put(“js”,“application/javascript”);
MIME_MAP.put(“json”,“application/json”);
MIME_MAP.put(“jpg”、“image/jpeg”);
MIME_MAP.put(“jpeg”、“图像/jpeg”);
MIME_MAP.put(“mp4”、“视频/mp4”);
MIME_MAP.put(“pdf”、“application/pdf”);
MIME_MAP.put(“png”,“image/png”);
MIME_MAP.put(“svg”、“image/svg+xml”);
MIME_MAP.put(“xlsm”,“application/vnd.openxmlformats of icedocument.spreadsheetml.sheet”);
MIME_MAP.put(“xml”,“application/xml”);
MIME_MAP.put(“zip”、“application/zip”);
MIME_MAP.put(“md”、“text/plain”);
MIME_MAP.put(“txt”,“text/plain”);
MIME_MAP.put(“php”,“text/plain”);
};
私有字符串文件系统根;
私有字符串前缀;
私有字符串目录索引;
/**
*@param url前缀所有URL的前缀。
*这是createContext的第一个参数。必须以斜杠开始和结束。
*@param filesystemRoot文件系统中的根目录。
*只有此目录下的文件才会提供给客户端。
*例如“/staticfiles”。
*@param directoryIndex文件,用于在请求目录时显示,例如“index.html”。
*/
公共StaticFileHandler(字符串URL前缀、字符串filesystemRoot、字符串目录索引){
如果(!urlPrefix.startsWith(“/”){
抛出新的RuntimeException(“pathPrefix不以斜杠开头”);
}
如果(!urlPrefix.endsWith(“/”)){
抛出新的RuntimeException(“pathPrefix不以斜杠结尾”);
}
this.urlPrefix=urlPrefix;
断言filesystemRoot.endsWith(“/”);
试一试{
this.filesystemRoot=新文件(filesystemRoot).getCanonicalPath();
}捕获(IOE异常){
抛出新的运行时异常(e);
}
this.directoryIndex=directoryIndex;
}
/**
*创建并注册一个新的静态文件处理程序。
*@param hs将在其中注册文件处理程序的HTTP服务器。
*@param path URL中以所有请求为前缀的路径,例如“/static/”
*@param filesystemRoot文件系统位置。
*例如“/var/www/mystaticfiles/”。
*将从文件系统文件“/var/www/mystaticfiles/x/y.html”提供对“/static/x/y.html”的请求
*@param directoryIndex文件,用于在请求目录时显示,例如“index.html”。
*/
公共静态void创建(HttpServer hs、字符串路径、字符串文件系统根、字符串目录索引){
StaticFileHandler sfh=新的StaticFileHandler(路径、filesystemRoot、目录索引);
hs.createContext(路径,sfh);
}
公共无效句柄(HttpExchange he)引发IOException{
String方法=he.getRequestMethod();
if(!((“HEAD.equals(方法)| |“GET.equals(方法))){
sendError(他,501,“不支持的HTTP方法”);
返回;
}
字符串wholeUrlPath=he.getRequestURI().getPath();
if(wholeUrlPath.endsWith(“/”)){
wholeUrlPath+=目录索引;
}
如果(!wholeUrlPath.startsWith(urlPrefix)){
抛出新的RuntimeException(“路径不在前缀中-路由错误?”);
}
字符串urlPath=wholeUrlPath.substring(urlPrefix.length());
文件f=新文件(filesystemRoot,urlPath);
文件规范文件;
试一试{
canonicalFile=f.getCanonicalFile();
}捕获(IOE异常){
//这可能更温和(即不是攻击,只是403),
//但我们不希望攻击者能够辨别出区别。
报告路径遍历(he);
返回;
}
字符串canonicalPath=canonicalFile.getPath();
如果(!canonicalPath.startsWith(filesystemRoot)){
报告路径遍历(he);
返回;
}
文件输入流fis;
试一试{
fis=新文件输入流(canonicalFile);
}catch(filenotfounde异常){
//该文件也可能被禁止使用,而不是丢失,但我们通过这种方式泄漏的信息越来越少
sendError(他,404,“未找到文件”);
返回;
}
字符串mimeType=lookupMime(urlPath);
he.getResponseHeaders().set(“内容类型”,mimeType);
if(“GET”.equals(方法)){
他
package de.phihag.miniticker;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class StaticFileHandler implements HttpHandler {
private static final Map<String,String> MIME_MAP = new HashMap<>();
static {
MIME_MAP.put("appcache", "text/cache-manifest");
MIME_MAP.put("css", "text/css");
MIME_MAP.put("gif", "image/gif");
MIME_MAP.put("html", "text/html");
MIME_MAP.put("js", "application/javascript");
MIME_MAP.put("json", "application/json");
MIME_MAP.put("jpg", "image/jpeg");
MIME_MAP.put("jpeg", "image/jpeg");
MIME_MAP.put("mp4", "video/mp4");
MIME_MAP.put("pdf", "application/pdf");
MIME_MAP.put("png", "image/png");
MIME_MAP.put("svg", "image/svg+xml");
MIME_MAP.put("xlsm", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
MIME_MAP.put("xml", "application/xml");
MIME_MAP.put("zip", "application/zip");
MIME_MAP.put("md", "text/plain");
MIME_MAP.put("txt", "text/plain");
MIME_MAP.put("php", "text/plain");
};
private String filesystemRoot;
private String urlPrefix;
private String directoryIndex;
/**
* @param urlPrefix The prefix of all URLs.
* This is the first argument to createContext. Must start and end in a slash.
* @param filesystemRoot The root directory in the filesystem.
* Only files under this directory will be served to the client.
* For instance "./staticfiles".
* @param directoryIndex File to show when a directory is requested, e.g. "index.html".
*/
public StaticFileHandler(String urlPrefix, String filesystemRoot, String directoryIndex) {
if (!urlPrefix.startsWith("/")) {
throw new RuntimeException("pathPrefix does not start with a slash");
}
if (!urlPrefix.endsWith("/")) {
throw new RuntimeException("pathPrefix does not end with a slash");
}
this.urlPrefix = urlPrefix;
assert filesystemRoot.endsWith("/");
try {
this.filesystemRoot = new File(filesystemRoot).getCanonicalPath();
} catch (IOException e) {
throw new RuntimeException(e);
}
this.directoryIndex = directoryIndex;
}
/**
* Create and register a new static file handler.
* @param hs The HTTP server where the file handler will be registered.
* @param path The path in the URL prefixed to all requests, such as "/static/"
* @param filesystemRoot The filesystem location.
* For instance "/var/www/mystaticfiles/".
* A request to "/static/x/y.html" will be served from the filesystem file "/var/www/mystaticfiles/x/y.html"
* @param directoryIndex File to show when a directory is requested, e.g. "index.html".
*/
public static void create(HttpServer hs, String path, String filesystemRoot, String directoryIndex) {
StaticFileHandler sfh = new StaticFileHandler(path, filesystemRoot, directoryIndex);
hs.createContext(path, sfh);
}
public void handle(HttpExchange he) throws IOException {
String method = he.getRequestMethod();
if (! ("HEAD".equals(method) || "GET".equals(method))) {
sendError(he, 501, "Unsupported HTTP method");
return;
}
String wholeUrlPath = he.getRequestURI().getPath();
if (wholeUrlPath.endsWith("/")) {
wholeUrlPath += directoryIndex;
}
if (! wholeUrlPath.startsWith(urlPrefix)) {
throw new RuntimeException("Path is not in prefix - incorrect routing?");
}
String urlPath = wholeUrlPath.substring(urlPrefix.length());
File f = new File(filesystemRoot, urlPath);
File canonicalFile;
try {
canonicalFile = f.getCanonicalFile();
} catch (IOException e) {
// This may be more benign (i.e. not an attack, just a 403),
// but we don't want the attacker to be able to discern the difference.
reportPathTraversal(he);
return;
}
String canonicalPath = canonicalFile.getPath();
if (! canonicalPath.startsWith(filesystemRoot)) {
reportPathTraversal(he);
return;
}
FileInputStream fis;
try {
fis = new FileInputStream(canonicalFile);
} catch (FileNotFoundException e) {
// The file may also be forbidden to us instead of missing, but we're leaking less information this way
sendError(he, 404, "File not found");
return;
}
String mimeType = lookupMime(urlPath);
he.getResponseHeaders().set("Content-Type", mimeType);
if ("GET".equals(method)) {
he.sendResponseHeaders(200, canonicalFile.length());
OutputStream os = he.getResponseBody();
copyStream(fis, os);
os.close();
} else {
assert("HEAD".equals(method));
he.sendResponseHeaders(200, -1);
}
fis.close();
}
private void copyStream(InputStream is, OutputStream os) throws IOException {
byte[] buf = new byte[4096];
int n;
while ((n = is.read(buf)) >= 0) {
os.write(buf, 0, n);
}
}
private void sendError(HttpExchange he, int rCode, String description) throws IOException {
String message = "HTTP error " + rCode + ": " + description;
byte[] messageBytes = message.getBytes("UTF-8");
he.getResponseHeaders().set("Content-Type", "text/plain; charset=utf-8");
he.sendResponseHeaders(rCode, messageBytes.length);
OutputStream os = he.getResponseBody();
os.write(messageBytes);
os.close();
}
// This is one function to avoid giving away where we failed
private void reportPathTraversal(HttpExchange he) throws IOException {
sendError(he, 400, "Path traversal attempt detected");
}
private static String getExt(String path) {
int slashIndex = path.lastIndexOf('/');
String basename = (slashIndex < 0) ? path : path.substring(slashIndex + 1);
int dotIndex = basename.lastIndexOf('.');
if (dotIndex >= 0) {
return basename.substring(dotIndex + 1);
} else {
return "";
}
}
private static String lookupMime(String path) {
String ext = getExt(path).toLowerCase();
return MIME_MAP.getOrDefault(ext, "application/octet-stream");
}
}