ソース
SimpleWebDavServer.java
import com.sun.net.httpserver.*;
import java.io.*;
import java.net.InetSocketAddress;
import java.nio.file.*;
import java.util.stream.*;
public class SimpleWebDavServer {
public static void main(String[] args) throws Exception {
int port = 8080;
// Ensure rootDir is an absolute path to avoid ambiguity
String rootDir = Paths.get("./").toAbsolutePath().normalize().toString();
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
server.createContext("/", exchange -> {
String method = exchange.getRequestMethod();
// Construct the full path, ensuring it's within the rootDir and normalized
String requestPath = exchange.getRequestURI().getPath();
Path path = Paths.get(rootDir, requestPath).normalize();
// Security check: ensure the resolved path is still within the root directory
if (!path.startsWith(rootDir)) {
exchange.sendResponseHeaders(403, -1); // Forbidden
return;
}
try {
switch (method) {
case "GET":
if (Files.exists(path)) {
if (Files.isDirectory(path)) {
// ディレクトリ一覧インデックス表示
StringBuilder html = new StringBuilder();
html.append("<html><body><h1>Index of ")
.append(exchange.getRequestURI().getPath())
.append("</h1><ul>");
try (Stream<Path> stream = Files.list(path)) {
stream.forEach(p -> {
String name = p.getFileName().toString();
html.append("<li><a href=\"")
.append(name)
.append(Files.isDirectory(p) ? "/" : "")
.append("\">")
.append(name)
.append(Files.isDirectory(p) ? "/" : "")
.append("</a></li>");
});
}
html.append("</ul></body></html>");
byte[] bytes = html.toString().getBytes("UTF-8");
exchange.getResponseHeaders().set("Content-Type", "text/html; charset=utf-8");
exchange.sendResponseHeaders(200, bytes.length);
exchange.getResponseBody().write(bytes);
} else {
exchange.sendResponseHeaders(200, Files.size(path));
Files.copy(path, exchange.getResponseBody());
}
} else {
exchange.sendResponseHeaders(404, -1);
}
break;
case "PUT":
if (Files.isDirectory(path)) {
exchange.sendResponseHeaders(400, -1); // Bad Request: Cannot PUT to a directory
break;
}
try {
// Create parent directories if they don't exist
Path parentDir = path.getParent();
if (parentDir != null && !Files.exists(parentDir)) {
Files.createDirectories(parentDir);
}
// Write the request body to the file
try (InputStream is = exchange.getRequestBody();
OutputStream os = Files.newOutputStream(path)) {
byte[] buffer = new byte[4096];
int len;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
}
exchange.sendResponseHeaders(201, -1); // Created
} catch (FileAlreadyExistsException e) {
// This should ideally not happen if Files.newOutputStream overwrites,
// but good to catch specific file system errors.
System.err.println("Error during PUT request (FileAlreadyExistsException): " + e.getMessage());
e.printStackTrace();
exchange.sendResponseHeaders(409, -1); // Conflict
} catch (AccessDeniedException e) {
System.err.println("Error during PUT request (AccessDeniedException): " + e.getMessage());
e.printStackTrace();
exchange.sendResponseHeaders(403, -1); // Forbidden
} catch (IOException e) {
System.err.println("Error during PUT request: " + e.getMessage());
e.printStackTrace();
exchange.sendResponseHeaders(500, -1); // Internal Server Error
}
break;
case "DELETE":
Files.deleteIfExists(path);
exchange.sendResponseHeaders(204, -1);
break;
case "PROPFIND":
String response = "<?xml version=\"1.0\" ?>"
+ "<D:multistatus xmlns:D=\"DAV:\">"
+ "<D:response><D:href>" + exchange.getRequestURI()
+ "</D:href><D:propstat><D:prop><D:resourcetype/></D:prop>"
+ "<D:status>HTTP/1.1 200 OK</D:status></D:propstat>"
+ "</D:response></D:multistatus>";
byte[] bytes = response.getBytes("UTF-8");
exchange.getResponseHeaders().set("Content-Type", "application/xml; charset=utf-8");
exchange.sendResponseHeaders(207, bytes.length);
exchange.getResponseBody().write(bytes);
break;
default:
exchange.sendResponseHeaders(405, -1);
break;
}
} finally {
exchange.getResponseBody().close();
}
});
server.start();
System.out.println("WebDAVサーバー起動: http://localhost:" + port + "/ (root: " + rootDir + ")");
}
}
実行方法
java SimpleWebDavServer.java
テスト
PUTメソッドでファイルアップロード
curl -X PUT -T test.img http://localhost:8080/
GETメソッドでファイルダウンロード
curl -O http://localhost:8080/test.img
DELETEメソッドでファイル削除
curl -X DELETE http://localhost:8080/test.img
なんで作ったか
- curlでsftp経由ファイルがやりとりしているけどどうも遅い
- httpの方が暗号化してないからプライベートなマシン間ではこれくらいの浅いプロトコルでやった方が早いかなぁって
- sftpとの速度比較はまだやってない
- あとはJavaのソースファイルモードがめちゃくちゃ便利なので、Javaのペラ1ソースファイルでどこまで便利なツールが作れるか、という競技性が楽しい
- ソースファイルモードでやってる限りは、設定ファイルなんていらなくて、ソースの上の方の初期値をちょっといじるだけでいい。楽しい。