Last active
April 12, 2020 16:50
-
-
Save pranavgade20/700cde21c10fede1e28f59433b540dec to your computer and use it in GitHub Desktop.
A file server written in java. This was an educational project, and likely has a lot of bugs/issues. I have not followed the specification because I am lazy. Use at your own risk. If you do have any issues though, feel free to reach out to me, I'd e happy to help :)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Instructions: generate a keystore using: | |
keytool -genkey -v -keystore keystore -storepass password -keyalg RSA -keysize 2048 | |
If you change the keystore file name or the keystore password, make sure to do the same in code as well. | |
*/ | |
import java.io.*; | |
import java.net.*; | |
import java.nio.file.Files; | |
import javax.net.ssl.KeyManagerFactory; | |
import javax.net.ssl.SSLContext; | |
import javax.net.ssl.SSLServerSocket; | |
import java.io.FileInputStream; | |
import java.net.ServerSocket; | |
import java.security.KeyStore; | |
public class SSLServer { | |
final static int port = 8080; | |
static String keystore = "keystore"; | |
static String password = "password"; | |
public static void main(String[] args) throws Exception { | |
SSLContext ctx; | |
KeyManagerFactory kmf; | |
KeyStore ks; | |
char[] passphrase = password.toCharArray(); | |
ctx = SSLContext.getInstance("TLS"); | |
kmf = KeyManagerFactory.getInstance("SunX509"); | |
ks = KeyStore.getInstance("JKS"); | |
ks.load(new FileInputStream(keystore), passphrase); | |
kmf.init(ks, passphrase); | |
ctx.init(kmf.getKeyManagers(), null, null); | |
ServerSocket serverSocket = ctx.getServerSocketFactory().createServerSocket(port); | |
((SSLServerSocket)serverSocket).setNeedClientAuth(false); | |
System.out.println("Ready for connections-"); | |
while (true) { | |
Socket client = serverSocket.accept(); | |
Thread t = new Thread(new Handler(client)); | |
t.start(); | |
} | |
} | |
} | |
class Handler implements Runnable { | |
Socket client; | |
String filePath = "/"; | |
public Handler(Socket c) { | |
client = c; | |
} | |
public void run() { | |
try { | |
client.setSendBufferSize(4*1024*1024); | |
client.setTcpNoDelay(true); | |
client.setPerformancePreferences(0, 0, 1); | |
client.setSoLinger(true, 30); | |
client.setSoTimeout(60*60*1000); | |
System.out.println("Client connected from port: " + client.getPort()); | |
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream())); | |
String line; | |
line = in.readLine(); | |
while (line != null) { | |
if (line.startsWith("GET ")) { | |
String uri = URLDecoder.decode((line.split(" "))[1], "UTF-8"); | |
System.out.println("Client requested: " + uri); | |
File request = new File(filePath + uri); | |
if(request.exists()) { | |
if (request.isDirectory()) { | |
sendDirectory(request); | |
} else { | |
long fromByte = 0; | |
long toByte = 0; | |
while (line != null) { | |
if (line.startsWith("Range: bytes=")) { | |
String[] range = line.substring(13).split("-"); | |
fromByte = Long.parseLong(range[0]); | |
if (!line.endsWith("-")) toByte = Long.parseLong(range[1]); | |
if (line.contains(",")) throw new Exception("RequestNotSupportedException: Client requested a feature that is not supported. Gib pranavgade20 $69 and get this fixed."); | |
} | |
if (line.equals("")) break; | |
line = in.readLine(); | |
} | |
sendFile(request, fromByte, toByte); | |
} | |
} else { | |
sendError(404, "File not found."); | |
} | |
} | |
if (line.equals("")) break; | |
line = in.readLine(); | |
} | |
in.close(); | |
client.close(); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
private void sendFile(File file, long fromByte, long toByte) { | |
try { | |
OutputStream outputStream = client.getOutputStream(); | |
if (toByte == 0) { | |
toByte = Files.size(file.toPath()); | |
} | |
String headers = "HTTP/1.1 200 \r\n"; | |
if (fromByte > 0) headers = "HTTP/1.1 206 \r\n" + | |
"Content-Range: bytes " + fromByte + "-" + toByte + "/" + file.length() + "\r\n"; | |
headers += "Content-Type: " + Files.probeContentType(file.toPath()) + "\r\n" + | |
"Accept-Ranges: bytes" + "\r\n" + | |
"Connection: close\r\n" + | |
"Content-Length: " + (toByte - fromByte) + "\r\n" + | |
"\r\n"; | |
outputStream.write(headers.getBytes()); | |
BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(file)); | |
inputStream.skip(fromByte); | |
int bufSize = 16*1024*1024; | |
byte[] buf = new byte[(int)(((toByte-fromByte) < bufSize) ? (toByte-fromByte) : bufSize)]; | |
long i = Math.floorDiv(toByte - fromByte, bufSize); | |
try { | |
while (inputStream.read(buf) != -1 && i != 0) { | |
outputStream.write(buf); | |
i--; | |
} | |
if (i == 0) { | |
byte[] tempBuf = new byte[(int)((toByte - fromByte)%bufSize)]; | |
inputStream.read(tempBuf); | |
outputStream.write(tempBuf); | |
} | |
} catch (Exception e) { | |
System.out.println("Client probably paused download. Probably."); | |
} | |
inputStream.close(); | |
outputStream.close(); | |
} catch (Exception exception) { | |
exception.printStackTrace(); | |
} | |
} | |
private void sendDirectory(File folder) { | |
try { | |
DataOutputStream dos = new DataOutputStream(client.getOutputStream()); | |
PrintWriter out = new PrintWriter(dos); | |
out.print("HTTP/1.1 200 \r\n"); // Version & status code | |
out.print("Content-Type: text/html\r\n"); // The type of data | |
out.print("Connection: close\r\n"); // Will close stream | |
out.print("\r\n"); | |
out.print("<!DOCTYPE html>\n" + | |
"<html>\n" + | |
"<head>\n" + | |
"\t<title>File sender!</title>\n" + | |
"</head>\n" + | |
"<body>\n"); | |
File[] listOfFiles = folder.listFiles(); | |
if (listOfFiles == null || listOfFiles.length == 0) { | |
out.print("<a href=\"/\">No files found.</a>"); | |
} else { | |
for (int i = 0; i < listOfFiles.length; i++) { | |
if (listOfFiles[i].isFile()) { | |
out.println("<a href=\"" + listOfFiles[i].getPath() + "\">" + listOfFiles[i].getName() + "</a>"); | |
out.println("<br>"); | |
} else if (listOfFiles[i].isDirectory()) { | |
out.println("<a href=\"" + listOfFiles[i].getPath() + "\">" + listOfFiles[i].getName() + '/' + "</a>"); | |
out.println("<br>"); | |
} | |
} | |
} | |
out.print("</body>\n" + | |
"</html>\n"); | |
out.print("\r\n"); | |
out.close(); | |
dos.close(); | |
} catch (Exception exception) { | |
exception.printStackTrace(); | |
} | |
} | |
private void sendError(int errCode, String error) { | |
try{ | |
DataOutputStream dos = new DataOutputStream(client.getOutputStream()); | |
PrintWriter out = new PrintWriter(dos); | |
out.print("HTTP/1.1 " + Integer.toString(errCode) + " \r\n"); // Version & status code | |
out.print("Content-Type: text/html\r\n"); // The type of data | |
out.print("Connection: close\r\n"); // Will close stream | |
out.print("\r\n"); | |
out.print("<b> " + error + " </b>"); | |
out.print("\r\n"); | |
out.close(); | |
dos.close(); | |
} catch (Exception e) { | |
e.printStackTrace(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment