get fucked

main
Brett 2023-11-15 15:02:48 -05:00
parent 3ef04a43ea
commit 0ab477314e
7 changed files with 218 additions and 157 deletions

View File

@ -8,18 +8,18 @@ import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
public class FileChunkingWriter { public class ChunkedCompressedChecksumFileWriter {
private final DataOutputStream writer; private final DataOutputStream networkStreamWriter;
private final StreamingXXHash64 streamHash; private final StreamingXXHash64 streamHash;
private final DataInputStream fileReader; private final DataInputStream fileInputReader;
private final int bufferSize; private final int bufferSize;
private final long seed; private final long seed;
public FileChunkingWriter(DataOutputStream writer, DataInputStream fileReader, int bufferSize, long seed){ public ChunkedCompressedChecksumFileWriter(DataOutputStream networkStreamWriter, DataInputStream fileInputReader, int bufferSize, long seed){
this.writer = writer; this.networkStreamWriter = networkStreamWriter;
this.streamHash = FileUtil.XX_HASH_FACTORY.newStreamingHash64(seed); this.streamHash = FileUtil.XX_HASH_FACTORY.newStreamingHash64(seed);
this.fileReader = fileReader; this.fileInputReader = fileInputReader;
this.bufferSize = bufferSize; this.bufferSize = bufferSize;
this.seed = seed; this.seed = seed;
} }
@ -37,23 +37,27 @@ public class FileChunkingWriter {
ArrayData compressed = compress(uncompressed); ArrayData compressed = compress(uncompressed);
// write data // write data
writer.writeInt(uncompressed.length); writeHeader(uncompressed.length, compressed.getActualLength(), hash);
writer.writeInt(compressed.getActualLength()); networkStreamWriter.write(compressed.getData(), 0, compressed.getActualLength());
writer.writeLong(hash); networkStreamWriter.flush();
writer.write(compressed.getData(), 0, compressed.getActualLength());
writer.flush();
} }
public void close() throws IOException { public void close() throws IOException {
writer.writeInt(0); networkStreamWriter.writeInt(0);
writer.writeLong(streamHash.getValue()); networkStreamWriter.writeLong(streamHash.getValue());
writer.flush(); networkStreamWriter.flush();
}
private void writeHeader(int uncompressed, int compressed, long hash) throws IOException {
networkStreamWriter.writeInt(uncompressed);
networkStreamWriter.writeInt(compressed);
networkStreamWriter.writeLong(hash);
} }
private byte[] readSome() throws IOException { private byte[] readSome() throws IOException {
byte[] readBytes = new byte[Integer.min(fileReader.available(), bufferSize)]; byte[] readBytes = new byte[Integer.min(fileInputReader.available(), bufferSize)];
int totalRead = fileReader.read(readBytes); int totalRead = fileInputReader.read(readBytes);
assert(readBytes.length == totalRead); assert(readBytes.length == totalRead);
return readBytes; return readBytes;

View File

@ -1,12 +1,8 @@
package client; package client;
import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
import net.jpountz.lz4.LZ4FrameInputStream;
import net.jpountz.lz4.LZ4FrameOutputStream;
import server.Server; import server.Server;
import shared.ExceptionLogger; import shared.ExceptionLogger;
import shared.FileHeader; import shared.FileUtil;
import java.io.*; import java.io.*;
import java.net.Socket; import java.net.Socket;
@ -14,12 +10,6 @@ import java.util.*;
public class Client { public class Client {
public static class ClientInvalidUsageException extends RuntimeException {
public ClientInvalidUsageException(String str){
super(str);
}
}
private final Socket serverConnection; private final Socket serverConnection;
private final DataOutputStream out; private final DataOutputStream out;
private final DataInputStream in; private final DataInputStream in;
@ -30,19 +20,13 @@ public class Client {
in = new DataInputStream(new BufferedInputStream(serverConnection.getInputStream())); in = new DataInputStream(new BufferedInputStream(serverConnection.getInputStream()));
} }
void sendFile(String path){ public Client sendFile(String path){
if (new File(path).isDirectory())
throw new ClientInvalidUsageException("Unable to send directory. Did you mean sendDir()?");
System.out.println("Sending path " + path); System.out.println("Sending path " + path);
new FileHeader(path).write(out); FileUtil.write(path, out);
try { return this;
out.flush();
} catch (IOException e) {
ExceptionLogger.log(e);
}
} }
void sendDir(String path){ public Client sendDir(String path){
File p = new File(path); File p = new File(path);
ArrayDeque<File> filesToCheck = new ArrayDeque<>(Arrays.asList(Objects.requireNonNull(p.listFiles()))); ArrayDeque<File> filesToCheck = new ArrayDeque<>(Arrays.asList(Objects.requireNonNull(p.listFiles())));
while (!filesToCheck.isEmpty()) { while (!filesToCheck.isEmpty()) {
@ -52,9 +36,10 @@ public class Client {
} else } else
sendFile(f.getPath()); sendFile(f.getPath());
} }
return this;
} }
void close(){ public void close(){
try { try {
in.close(); in.close();
out.close(); out.close();
@ -66,7 +51,8 @@ public class Client {
public static void main(String[] args) { public static void main(String[] args) {
try { try {
new Client("localhost", Server.SERVER_PORT).sendDir("in/"); new Client("localhost", Server.SERVER_PORT).sendDir("in/").close();
//new Client("localhost", Server.SERVER_PORT).sendFile("in/ihaveafile.txt").close();
} catch (Exception e){ } catch (Exception e){
ExceptionLogger.log(e); ExceptionLogger.log(e);
} }

View File

@ -0,0 +1,69 @@
package server;
import net.jpountz.xxhash.StreamingXXHash64;
import shared.ArrayData;
import shared.FileUtil;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
public class ChunkedCompressedChecksumFileReader {
private final DataInputStream networkStreamReader;
private final StreamingXXHash64 streamHash;
private final DataOutputStream fileOutputWriter;
private final long seed;
public ChunkedCompressedChecksumFileReader(DataInputStream networkStreamReader, String fileOutputPath, long seed) throws IOException {
this.networkStreamReader = networkStreamReader;
this.streamHash = FileUtil.XX_HASH_FACTORY.newStreamingHash64(seed);
this.fileOutputWriter = new DataOutputStream(new BufferedOutputStream(Files.newOutputStream(Paths.get(fileOutputPath))));
this.seed = seed;
}
public FileHeader readChunk() throws IOException {
FileHeader header = readHeader();
if (header.getUncompressed() == 0)
return header;
byte[] data = readSome(header);
byte[] decompressed = decompress(header, data);
hash(header, decompressed);
fileOutputWriter.write(decompressed, 0, decompressed.length);
return header;
}
public void close() throws IOException {
long streamHash = networkStreamReader.readLong();
if (streamHash != this.streamHash.getValue())
throw new RuntimeException("Stream total hash doesn't match the client's sent hash!");
fileOutputWriter.flush();
fileOutputWriter.close();
}
private FileHeader readHeader() throws IOException {
return new FileHeader().read(networkStreamReader);
}
private byte[] readSome(FileHeader header) throws IOException {
byte[] data = new byte[header.getCompressed()];
int amount = networkStreamReader.read(data, 0, header.getCompressed());
assert (header.getCompressed() == amount);
return data;
}
private byte[] decompress(FileHeader header, byte[] data) {
byte[] restored = new byte[header.getUncompressed()];
int len = FileUtil.DECOMPRESSOR.decompress(data, 0, restored, 0, header.getUncompressed());
assert (header.getUncompressed() == len);
return restored;
}
private void hash(FileHeader header, byte[] data) {
long computedHash = FileUtil.HASH_64.hash(data, 0, data.length, seed);
streamHash.update(data, 0, data.length);
if (computedHash != header.getHash())
throw new RuntimeException("Computed hash doesn't match sent hash! File corrupted?");
}
}

View File

@ -1,11 +1,7 @@
package server; package server;
import net.jpountz.lz4.LZ4BlockInputStream;
import net.jpountz.lz4.LZ4BlockOutputStream;
import net.jpountz.lz4.LZ4FrameInputStream;
import net.jpountz.lz4.LZ4FrameOutputStream;
import shared.ExceptionLogger; import shared.ExceptionLogger;
import shared.FileHeader; import shared.FileUtil;
import java.io.*; import java.io.*;
import java.net.Socket; import java.net.Socket;
@ -37,8 +33,8 @@ public class Connection implements Runnable {
if (in.available() > 0) { if (in.available() > 0) {
byte command = in.readByte(); byte command = in.readByte();
if (command == FileHeader.COMMAND.WRITE.type) if (command == FileUtil.COMMAND.WRITE.type)
FileHeader.receive(in); FileUtil.receive(in);
} }
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);

View File

@ -0,0 +1,36 @@
package server;
import shared.ExceptionLogger;
import java.io.DataInputStream;
import java.io.IOException;
public class FileHeader {
private int uncompressed;
private int compressed;
private long hash;
public FileHeader() {}
public FileHeader read(DataInputStream reader) throws IOException{
uncompressed = reader.readInt();
if (uncompressed == 0)
return this;
compressed = reader.readInt();
hash = reader.readLong();
return this;
}
public int getUncompressed() {
return uncompressed;
}
public int getCompressed() {
return compressed;
}
public long getHash() {
return hash;
}
}

View File

@ -1,111 +0,0 @@
package shared;
import client.Client;
import client.FileChunkingWriter;
import net.jpountz.xxhash.StreamingXXHash64;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
public class FileHeader {
public enum COMMAND {
WRITE((byte) 1);
public final byte type;
COMMAND(byte type) {
this.type = type;
}
}
private final String relative_path;
private final String full_path;
public FileHeader(String path) {
File pf = new File(path);
if (!pf.exists())
throw new Client.ClientInvalidUsageException("Unable to send a file which doesn't exist!");
if (pf.isDirectory())
throw new Client.ClientInvalidUsageException("Path is a directory unable to send!");
String workingDirectory = System.getProperty("user.dir");
this.full_path = path;
this.relative_path = path.replace(workingDirectory, "");
System.out.println(relative_path);
}
public void write(DataOutputStream dataOut) {
try {
DataInputStream fileReader = new DataInputStream(new BufferedInputStream(Files.newInputStream(Paths.get(full_path))));
dataOut.writeByte(COMMAND.WRITE.type);
dataOut.writeUTF(relative_path);
FileChunkingWriter writer = new FileChunkingWriter(dataOut, fileReader, FileUtil.READER_SIZE, FileUtil.SEED);
while (fileReader.available() > 0)
writer.processChunk();
writer.close();
fileReader.close();
} catch (Exception e) {
ExceptionLogger.log(e);
}
}
public static void receive(DataInputStream reader) {
try {
String path = createPath(reader.readUTF());
System.out.println("Writing to file: " + path);
DataOutputStream writer = new DataOutputStream(new BufferedOutputStream(Files.newOutputStream(Paths.get(path))));
StreamingXXHash64 computedStreamHash = FileUtil.XX_HASH_FACTORY.newStreamingHash64(FileUtil.SEED);
while (true) {
int uncompressed_size = reader.readInt();
if (uncompressed_size <= 0)
break;
int compressed_size = reader.readInt();
long hash = reader.readLong();
byte[] data = new byte[compressed_size];
int amount = reader.read(data, 0, compressed_size);
assert(amount == compressed_size);
byte[] restored = new byte[uncompressed_size];
int len = FileUtil.DECOMPRESSOR.decompress(data, 0, restored, 0, uncompressed_size);
assert(len == uncompressed_size);
long computedHash = FileUtil.HASH_64.hash(restored, 0, uncompressed_size, FileUtil.SEED);
computedStreamHash.update(restored, 0, uncompressed_size);
if (hash != computedHash)
throw new RuntimeException(hash + " HELP! " + computedHash);
writer.write(restored, 0, uncompressed_size);
}
long streamHash = reader.readLong();
if (computedStreamHash.getValue() != streamHash)
throw new RuntimeException("HELP 22");
writer.flush();
writer.close();
} catch (Exception e) {
ExceptionLogger.log(e);
}
}
private static String createPath(String userFile) {
String[] pathParts = userFile.split("/");
String userDirectory = userFile.replace(pathParts[pathParts.length - 1], "");
File ld = new File(System.getProperty("user.dir") + "/write/" + userDirectory);
if (!ld.exists() && !ld.mkdirs())
System.out.println("Failed to create directory");
return System.getProperty("user.dir") + "/write/" + userFile;
}
}

View File

@ -1,13 +1,20 @@
package shared; package shared;
import client.ChunkedCompressedChecksumFileWriter;
import net.jpountz.lz4.LZ4Compressor; import net.jpountz.lz4.LZ4Compressor;
import net.jpountz.lz4.LZ4Factory; import net.jpountz.lz4.LZ4Factory;
import net.jpountz.lz4.LZ4FastDecompressor; import net.jpountz.lz4.LZ4FastDecompressor;
import net.jpountz.xxhash.XXHash64; import net.jpountz.xxhash.XXHash64;
import net.jpountz.xxhash.XXHashFactory; import net.jpountz.xxhash.XXHashFactory;
import server.ChunkedCompressedChecksumFileReader;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
public class FileUtil { public class FileUtil {
// do not change it breaks stuff
protected static final int READER_SIZE = 8192; protected static final int READER_SIZE = 8192;
public static final long SEED = 691; public static final long SEED = 691;
@ -18,5 +25,79 @@ public class FileUtil {
public static final XXHashFactory XX_HASH_FACTORY = XXHashFactory.fastestInstance(); public static final XXHashFactory XX_HASH_FACTORY = XXHashFactory.fastestInstance();
public static final XXHash64 HASH_64 = XX_HASH_FACTORY.hash64(); public static final XXHash64 HASH_64 = XX_HASH_FACTORY.hash64();
public enum COMMAND {
WRITE((byte) 1);
public final byte type;
COMMAND(byte type) {
this.type = type;
}
}
public static void write(String path, DataOutputStream dataOut) {
validatePath(path);
String relative_path = path.replace(System.getProperty("user.dir"), "");
try {
DataInputStream fileReader = new DataInputStream(new BufferedInputStream(Files.newInputStream(Paths.get(path))));
dataOut.writeByte(COMMAND.WRITE.type);
dataOut.writeUTF(relative_path);
ChunkedCompressedChecksumFileWriter writer = new ChunkedCompressedChecksumFileWriter(dataOut, fileReader, FileUtil.READER_SIZE, FileUtil.SEED);
while (fileReader.available() > 0)
writer.processChunk();
writer.close();
fileReader.close();
} catch (Exception e) {
ExceptionLogger.log(e);
}
}
public static void receive(DataInputStream dataIn) {
try {
String path = createPath(dataIn.readUTF());
System.out.println("Writing to file: " + path);
ChunkedCompressedChecksumFileReader reader = new ChunkedCompressedChecksumFileReader(dataIn, path, FileUtil.SEED);
// ugh I want while(reader.readChunk().getUncompressed()); but it makes warnings!!!
while(true) {
if (reader.readChunk().getUncompressed() == 0)
break;
}
reader.close();
System.out.println("Writing " + path + " complete");
} catch (Exception e) {
ExceptionLogger.log(e);
}
}
public static class InvalidUsageException extends RuntimeException {
public InvalidUsageException(String str) {
super(str);
}
}
private static void validatePath(String path) {
File pf = new File(path);
if (!pf.exists())
throw new InvalidUsageException("Unable to send a file which doesn't exist!");
if (pf.isDirectory())
throw new InvalidUsageException("Path is a directory unable to send! Did you mean sendDir()?");
}
private static String createPath(String userFile) {
String[] pathParts = userFile.split("/");
String userDirectory = userFile.replace(pathParts[pathParts.length - 1], "");
File ld = new File(System.getProperty("user.dir") + "/write/" + userDirectory);
if (!ld.exists() && !ld.mkdirs())
throw new RuntimeException("Failed to create directory");
return System.getProperty("user.dir") + "/write/" + userFile;
}
} }