Newer
Older
KeeperJerry_Launcher / LaunchServer / source / response / update / UpdateResponse.java
@KeeperJerry KeeperJerry on 27 Jun 2020 5 KB Рефактор кода
package launchserver.response.update;

import launcher.hasher.HashedDir;
import launcher.hasher.HashedEntry;
import launcher.hasher.HashedEntry.Type;
import launcher.helper.IOHelper;
import launcher.request.update.UpdateRequest;
import launcher.request.update.UpdateRequest.Action;
import launcher.serialize.HInput;
import launcher.serialize.HOutput;
import launcher.serialize.signed.SignedObjectHolder;
import launchserver.LaunchServer;
import launchserver.response.Response;

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;

public final class UpdateResponse extends Response
{
    public UpdateResponse(LaunchServer server, long id, HInput input, HOutput output)
    {
        super(server, id, input, output);
    }

    @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;
        Action[] actionsSlice = new Action[UpdateRequest.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 Action(input);
            }

            // Perform actions
            for (int i = 0; i < length; i++)
            {
                Action 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();
        }
    }
}