Launcher/LaunchServer/src/main/java/ru/gravit/launchserver/response/update/UpdateResponse.java

125 lines
4.9 KiB
Java

package ru.gravit.launchserver.response.update;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Path;
import java.util.Deque;
import java.util.LinkedList;
import java.util.zip.DeflaterOutputStream;
import ru.gravit.launcher.hasher.HashedDir;
import ru.gravit.launcher.hasher.HashedEntry;
import ru.gravit.launcher.hasher.HashedEntry.Type;
import ru.gravit.launcher.helper.IOHelper;
import ru.gravit.launcher.request.UpdateAction;
import ru.gravit.launcher.serialize.HInput;
import ru.gravit.launcher.serialize.HOutput;
import ru.gravit.launcher.serialize.SerializeLimits;
import ru.gravit.launcher.serialize.signed.SignedObjectHolder;
import ru.gravit.launchserver.LaunchServer;
import ru.gravit.launchserver.response.Response;
public final class UpdateResponse extends Response {
public UpdateResponse(LaunchServer server, long session, HInput input, HOutput output, String ip) {
super(server, session, input, output, ip);
}
@Override
public void reply() throws IOException {
// Read update dir name
String updateDirName = IOHelper.verifyFileName(input.readString(255));
SignedObjectHolder<HashedDir> hdir = server.getUpdateDir(updateDirName);
if (hdir == null) {
requestError(String.format("Unknown update dir: %s", updateDirName));
return;
}
writeNoError(output);
// Write update hdir
debug("Update dir: '%s'", updateDirName);
hdir.write(output);
output.writeBoolean(server.config.compress);
output.flush();
// Prepare variables for actions queue
Path dir = server.updatesDir.resolve(updateDirName);
Deque<HashedDir> dirStack = new LinkedList<>();
dirStack.add(hdir.object);
// Perform update
// noinspection IOResourceOpenedButNotSafelyClosed
OutputStream fileOutput = server.config.compress ? new DeflaterOutputStream(output.stream, IOHelper.newDeflater(), IOHelper.BUFFER_SIZE, true) : output.stream;
UpdateAction[] actionsSlice = new UpdateAction[SerializeLimits.MAX_QUEUE_SIZE];
loop:
while (true) {
// Read actions slice
int length = input.readLength(actionsSlice.length);
for (int i = 0; i < length; i++)
actionsSlice[i] = new UpdateAction(input);
// Perform actions
for (int i = 0; i < length; i++) {
UpdateAction action = actionsSlice[i];
switch (action.type) {
case CD:
debug("CD '%s'", action.name);
// Get hashed dir (for validation)
HashedEntry hSubdir = dirStack.getLast().getEntry(action.name);
if (hSubdir == null || hSubdir.getType() != Type.DIR)
throw new IOException("Unknown hashed dir: " + action.name);
dirStack.add((HashedDir) hSubdir);
// Resolve dir
dir = dir.resolve(action.name);
break;
case GET:
debug("GET '%s'", action.name);
// Get hashed file (for validation)
HashedEntry hFile = dirStack.getLast().getEntry(action.name);
if (hFile == null || hFile.getType() != Type.FILE)
throw new IOException("Unknown hashed file: " + action.name);
// Resolve and write file
Path file = dir.resolve(action.name);
if (IOHelper.readAttributes(file).size() != hFile.size()) {
fileOutput.write(0x0);
fileOutput.flush();
throw new IOException("Unknown hashed file: " + action.name);
}
fileOutput.write(0xFF);
try (InputStream fileInput = IOHelper.newInput(file)) {
IOHelper.transfer(fileInput, fileOutput);
}
break;
case CD_BACK:
debug("CD ..");
// Remove from hashed dir stack
dirStack.removeLast();
if (dirStack.isEmpty())
throw new IOException("Empty hDir stack");
// Get parent
dir = dir.getParent();
break;
case FINISH:
break loop;
default:
throw new AssertionError(String.format("Unsupported action type: '%s'", action.type.name()));
}
}
// Flush all actions
fileOutput.flush();
}
// So we've updated :)
if (fileOutput instanceof DeflaterOutputStream)
((DeflaterOutputStream) fileOutput).finish();
}
}