Skip to content

Instantly share code, notes, and snippets.

@pranavgade20
Last active April 12, 2020 16:50
Show Gist options
  • Save pranavgade20/700cde21c10fede1e28f59433b540dec to your computer and use it in GitHub Desktop.
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 :)
/* 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