I've been working on a basic web server from scratch using java. I was able to get most of the functionality working correctly. Right now, I am struggling with listing the default directory & files when you visit the localhost: by default. As in, I want to see a list of the directories and make their file paths clickable w/html a tags. How can I list all files and directories as a landing page for my java web server? The code that I wrote is below.
WebServer.java
import java.net.*;
import java.io.*;
import java.util.*;
import java.lang.Runtime;
class WebServer {
private static String WEB_ROOT = "./";
private static Socket socket;
private static InputStream inputStream;
private static OutputStream outputStream;
private static DataOutputStream dataOutputStream;
private static HTTPRequestParser httpRequestParser;
public static void main(String args[]) {
// Create Server Socket, listen on port passed in via Command Console
// Use: java WebServer <port number>
ServerSocket serverSocket;
int port;
if (args.length != 1) {
System.err.println("Use: java WebServer <port number>");
return;
}
port = Integer.parseInt(args[0]);
try {
serverSocket = new ServerSocket(port);
System.out.println("Server listening on port " + port);
} catch (IOException e) {
System.err.println("Unable to listen on port " + port + ": " + e.getMessage());
return;
}
// Server listens for incoming requests
while (true) {
// Wait client to connect
try {
socket = serverSocket.accept();
} catch (IOException e) {
System.err.println("Unable to accept connection: " + e.getMessage());
continue;
}
System.out.println("Connection accepted.");
try {
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
dataOutputStream = new DataOutputStream(outputStream);
handleRequests();
socket.close();
System.out.println("Connection closed\n");
} catch (IOException e) {
System.err.println("Unable to read/write: " + e.getMessage());
}
}
}
private static void handleRequests() throws IOException {
httpRequestParser = new HTTPRequestParser(inputStream);
// GET or POST REQUEST ONLY
String requestMethod = httpRequestParser.getRequestMethod();
if (!(requestMethod.equals("GET") || requestMethod.equals("POST"))) {
invalidRequestErr();
return;
}
String fileName = httpRequestParser.getFileName();
String filePath = WEB_ROOT + fileName;
File file = new File(filePath);
// File permission or not found error.
if (!file.exists()) {
fileNotFoundError(fileName);
return;
}
if (!file.canRead()) {
forbiddenAccessError(fileName);
return;
}
// Assume everything is OK then. Send back a reply.
dataOutputStream.writeBytes("HTTP/1.1 200 OK\r\n");
String queryString = httpRequestParser.getQueryString();
if (fileName.endsWith("pl")) {
Process p;
String env = "REQUEST_METHOD=" + requestMethod + " ";
if (requestMethod.equals("POST")) {
env += "CONTENT_TYPE=" + httpRequestParser.getContentType() + " " +
"CONTENT_LENGTH=" + Integer.toString(httpRequestParser.getContentLength()) + " ";
} else {
env += "QUERY_STRING=" + queryString + " ";
}
p = Runtime.getRuntime().exec("/usr/bin/env " + env +
"/usr/bin/perl " + filePath);
if (requestMethod.equals("POST")) {
// Pass form data into Perl process
DataOutputStream o = new DataOutputStream(p.getOutputStream());
o.writeBytes(httpRequestParser.getFormData() + "\r\n");
o.close();
}
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
// Write response content
String l;
while ((l = br.readLine()) != null) {
dataOutputStream.writeBytes(l + "\r\n");
}
dataOutputStream.writeBytes("\r\n");
} else {
staticFileRequests(filePath);
}
dataOutputStream.flush();
}
private static void staticFileRequests(String filePath) {
try {
if (filePath.endsWith(".html")) {
dataOutputStream.writeBytes("Content-type: text/html\r\n");
} else if (filePath.endsWith(".jpg")) {
dataOutputStream.writeBytes("Content-type: image/jpeg\r\n");
} else if (filePath.endsWith("gif")) {
dataOutputStream.writeBytes("Content-type: image/gif\r\n");
} else if (filePath.endsWith("css")) {
dataOutputStream.writeBytes("Content-type: text/css\r\n");
}
dataOutputStream.writeBytes("\r\n");
// Read content at 1KB rate.
File file = new File(filePath);
byte[] buffer = new byte[(int)file.length()];
FileInputStream fis = new FileInputStream(file);
int size = fis.read(buffer);
while (size > 0) {
dataOutputStream.write(buffer, 0, size);
size = fis.read(buffer);
}
} catch (IOException e) {
System.err.println("Unable to READ/WRITE: " + e.getMessage());
}
}
private static void invalidRequestErr() throws IOException {
String errorMessage = "This Web Server only demonstrates GET or POST requests\r\n";
dataOutputStream.writeBytes("HTTP/1.1 400 Bad Request\r\n");
dataOutputStream.writeBytes("Content-length: " + errorMessage.length() + "\r\n\r\n");
dataOutputStream.writeBytes(errorMessage);
}
private static void fileNotFoundError(String fileName) throws IOException {
String errorMessage = "Unable to find " + fileName + " on this server.\r\n";
dataOutputStream.writeBytes("HTTP/1.1 404 Not Found\r\n");
dataOutputStream.writeBytes("Content-length: " + errorMessage.length() + "\r\n\r\n");
dataOutputStream.writeBytes(errorMessage);
}
private static void forbiddenAccessError(String fileName) throws IOException {
String errorMessage = "You need permission to access " + fileName + " on this server.\r\n";
dataOutputStream.writeBytes("HTTP/1.1 403 Forbidden\r\n");
dataOutputStream.writeBytes("Content-length: " + errorMessage.length() + "\r\n\r\n");
dataOutputStream.writeBytes(errorMessage);
}
}
class HTTPRequestParser {
private String requestMethod, fileName, queryString, formData;
private Hashtable<String, String> headers;
private int[] ver;
public HTTPRequestParser(InputStream is) {
BufferedReader br = new BufferedReader(new InputStreamReader(is));
requestMethod = "";
fileName = "";
queryString = "";
formData = "";
headers = new Hashtable<String, String>();
try {
// Wait for HTTP request from the connection
String line = br.readLine();
// Bail out if line is null. In case some client tries to be
// funny and close immediately after connection. (I am
// looking at you, Chrome!)
if (line == null) {
return;
}
// Shows Client requests in server log.
System.out.println("Request: " + line);
String tokens[] = line.split(" ");
requestMethod = tokens[0];
if (tokens[1].contains("?")) {
String urlComponents[] = tokens[1].split("\\?");
fileName = urlComponents[0];
queryString = urlComponents[1];
} else {
fileName = tokens[1];
}
// Read/Parse HTTP headers
int idx;
line = br.readLine();
while (!line.equals("")) {
idx = line.indexOf(":");
if (idx < 0) {
headers = null;
break;
} else {
headers.put(line.substring(0, idx).toLowerCase(),
line.substring(idx+1).trim());
}
line = br.readLine();
}
// Read POST Data
if (requestMethod.equals("POST")) {
int contentLength = getContentLength();
final char[] data = new char[contentLength];
for (int i = 0; i < contentLength; i++) {
data[i] = (char) br.read();
}
formData = new String(data);
}
} catch (IOException e) {
System.err.println("Unable to READ | WRITE: " + e.getMessage());
}
}
public String getRequestMethod() {
return requestMethod;
}
public String getFileName() {
return fileName;
}
public String getQueryString() {
return queryString;
}
public String getContentType() {
return headers.get("content-type");
}
public int getContentLength() {
return Integer.parseInt(headers.get("content-length"));
}
public String getFormData() {
return formData;
}
public static List<File> fileList(String directoryName) {
File directory = new File(directoryName);
List<File> resultList = new ArrayList<File>();
// get all the files from the directory
File[] fileList = directory.listFiles();
for(File file : fileList) {
if (file.isFile()) {
System.out.println(file.getAbsolutePath());
} else if (file.isDirectory()) {
resultList.addAll(fileList(file.getAbsolutePath()));
}
}
return resultList;
}
}
答案 0 :(得分:0)
首先,&#34;着陆页&#34;你说的是URL(/)中的根路径,与代码中的WEB_ROOT
相同。这种情况得到了很好的处理,因此无需担心
这里唯一的问题是如何将子文件夹或文件列表生成为网页。您需要使用Java代码获取文件夹和文件的列表,然后将它们组合成HTML代码。就像你在fileList
方法中所做的那样
HTML是一种标记语言。要构造它,只需将HTML标记插入字符串,然后通过HTTP发送它就像文件一样
<a>
标签的简单语法是:<a href="path">display content</a>
,此文件服务器依赖于相对路径,因此我们需要计算根路径和文件/子文件夹路径之间的相对路径,我们将添加一个&#34; /&#34;在相对路径之前声明这是主机上的相对路径(http://localhost:port)
这是一个简单的代码,可以产生这个:
private static void folderRequests(File folder) {
try {
dataOutputStream.writeBytes("Content-type: text/html\r\n");
dataOutputStream.writeBytes("\r\n");
dataOutputStream.writeBytes("<html><body>"); // html open tag
for (File file : folder.listFiles()) {
// calculate relative path
Path rootPath = Paths.get(new File(WEB_ROOT).toURI()); // this can be static
Path filePath = Paths.get(file.toURI());
String pathString = rootPath.relativize(filePath).toString();
// construct html
dataOutputStream.writeBytes("<a href=\"/" + pathString + "\">"
+ file.getName() + "</a><br>");
}
dataOutputStream.writeBytes("</body></html>"); // html close tag
} catch (IOException e) {
e.printStackTrace();
}
}
将staticFileRequests(filePath);
替换为:
if (file.isDirectory()) {
folderRequests(file);
} else {
staticFileRequests(filePath);
}
请注意,您可能需要注意几个问题:
- 有些文件夹包含空格,我很懒,所以我在格式化为HTML时忽略编码(你可以通过使用类URLEncoder
明确地做到这一点。)但是,在处理请求时,如果有&sa; sa文件夹命名&#34;文件夹&#34;,浏览器将访问&#34; http://localhost:port/a%20folder&#34;而不是&#34; http://localhost:port/a文件夹&#34;。您需要添加一些代码来处理它。有关详细信息,请参阅https://en.wikipedia.org/wiki/Percent-encoding
- 您可能希望使用UTF-8字符集来支持更多字符。
- 有关计算相对路径的信息,请参阅:How to construct a relative path in Java from two absolute paths (or URLs)?