All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.mike10004.harreplay.vhsimpl.VhsReplayManager Maven / Gradle / Ivy

The newest version!
package io.github.mike10004.harreplay.vhsimpl;

import com.google.common.net.HostAndPort;
import com.browserup.harreader.HarReader;
import com.browserup.harreader.HarReaderException;
import com.browserup.harreader.model.HarEntry;
import io.github.mike10004.harreplay.ReplayManager;
import io.github.mike10004.harreplay.ReplayServerConfig;
import io.github.mike10004.harreplay.ReplayServerConfig.Replacement;
import io.github.mike10004.harreplay.ReplayServerConfig.ResponseHeaderTransform;
import io.github.mike10004.harreplay.ReplaySessionConfig;
import io.github.mike10004.harreplay.ReplaySessionControl;
import io.github.mike10004.vhs.BasicHeuristic;
import io.github.mike10004.vhs.EntryMatcher;
import io.github.mike10004.vhs.EntryMatcherFactory;
import io.github.mike10004.vhs.EntryParser;
import io.github.mike10004.vhs.HarBridgeEntryParser;
import io.github.mike10004.vhs.HarResponseEncoderFactory;
import io.github.mike10004.vhs.HeuristicEntryMatcher;
import io.github.mike10004.vhs.ResponseInterceptor;
import io.github.mike10004.vhs.VirtualHarServer;
import io.github.mike10004.vhs.VirtualHarServerControl;
import io.github.mike10004.vhs.bmp.BmpResponseListener;
import io.github.mike10004.vhs.bmp.BmpResponseManufacturer;
import io.github.mike10004.vhs.bmp.BrowsermobVhsConfig;
import io.github.mike10004.vhs.bmp.BrowsermobVirtualHarServer;
import io.github.mike10004.vhs.bmp.HarReplayManufacturer;
import io.github.mike10004.vhs.bmp.KeystoreData;
import io.github.mike10004.vhs.bmp.NanohttpdTlsEndpointFactory;
import io.github.mike10004.vhs.bmp.ScratchDirProvider;
import io.github.mike10004.vhs.harbridge.sstoehr.SstoehrHarBridge;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

import static java.util.Objects.requireNonNull;

public class VhsReplayManager implements ReplayManager {

    private VhsReplayManagerConfig config;
    private final EntryMatcherFactory entryMatcherFactory;

    public VhsReplayManager() {
        this(VhsReplayManagerConfig.getDefault());
    }

    public VhsReplayManager(VhsReplayManagerConfig config) {
        this(config, HeuristicEntryMatcher.factory(new BasicHeuristic(), BasicHeuristic.DEFAULT_THRESHOLD_EXCLUSIVE));
    }

    protected VhsReplayManager(VhsReplayManagerConfig config, EntryMatcherFactory entryMatcherFactory) {
        this.config = requireNonNull(config, "config");
        this.entryMatcherFactory = requireNonNull(entryMatcherFactory, "entryMatcherFactory");
    }

    protected EntryParser createHarEntryParser() {
        HarResponseEncoderFactory responseEncoderFactory = HarResponseEncoderFactory.alwaysIdentityEncoding();
        return new HarBridgeEntryParser<>(new SstoehrHarBridge(), responseEncoderFactory);
    }

    protected EntryMatcher buildHarEntryMatcher(ReplaySessionConfig sessionConfig) throws IOException {
        HarReader harReader = config.harReaderFactory.createReader();
        List entries;
        try {
            entries = harReader.readFromFile(sessionConfig.harFile).getLog().getEntries();
        } catch (HarReaderException e) {
            throw new IOException(e);
        }
        EntryParser parser = createHarEntryParser();
        EntryMatcher harEntryMatcher = entryMatcherFactory.createEntryMatcher(entries, parser);
        return harEntryMatcher;
    }

    @Override
    public ReplaySessionControl start(ReplaySessionConfig sessionConfig) throws IOException {
        EntryMatcher harEntryMatcher = buildHarEntryMatcher(sessionConfig);
        EntryMatcher compositeEntryMatcher = enhanceEntryMatcherFromConfig(harEntryMatcher, sessionConfig.replayServerConfig);
        List interceptors = new ArrayList<>();
        interceptors.addAll(buildInterceptorsForReplacements(sessionConfig.replayServerConfig.replacements));
        interceptors.addAll(buildInterceptorsForTransforms(sessionConfig.replayServerConfig.responseHeaderTransforms));
        int port = sessionConfig.port;
        VirtualHarServer vhs = createVirtualHarServer(port, sessionConfig.scratchDir, compositeEntryMatcher, interceptors, config.bmpResponseListener);
        VirtualHarServerControl ctrl = vhs.start();
        Runnable stopListener = () -> {
            sessionConfig.serverTerminationCallbacks.forEach(c -> {
                c.terminated(null);
            });
        };
        return new VhsReplaySessionControl(ctrl, true, stopListener);
    }

    protected BmpResponseManufacturer createResponseManufacturer(EntryMatcher entryMatcher, Iterable responseInterceptors) {
        return new HarReplayManufacturer(entryMatcher, responseInterceptors);
    }

    protected VirtualHarServer createVirtualHarServer(int port, Path scratchParentDir, EntryMatcher entryMatcher, Iterable responseInterceptors, BmpResponseListener bmpResponseListener) throws IOException {
        try {
            KeystoreData keystoreData = config.keystoreGenerator.generate("localhost");
            BmpResponseManufacturer responseManufacturer = createResponseManufacturer(entryMatcher, responseInterceptors);
            BrowsermobVhsConfig.Builder configBuilder = BrowsermobVhsConfig.builder(responseManufacturer)
                    .port(port)
                    .responseListener(bmpResponseListener)
                    .tlsEndpointFactory(NanohttpdTlsEndpointFactory.create(keystoreData, null))
                    .scratchDirProvider(ScratchDirProvider.under(scratchParentDir));
            BrowsermobVhsConfig config = configBuilder.build();
            return new BrowsermobVirtualHarServer(config);
        } catch (GeneralSecurityException e) {
            throw new IOException(e);
        }
    }

    protected List buildInterceptorsForReplacements(Collection replacements) {
        return replacements.stream().map(replacement ->  new ReplacingInterceptor(config, replacement)).collect(Collectors.toList());
    }

    protected List buildInterceptorsForTransforms(Collection headerTransforms) {
        return headerTransforms.stream().map(headerTransform -> new HeaderTransformInterceptor(config, headerTransform)).collect(Collectors.toList());
    }

    protected EntryMatcher enhanceEntryMatcherFromConfig(EntryMatcher harEntryMatcher, ReplayServerConfig serverConfig) {
        MappingEntryMatcher mappingEntryMatcher = new MappingEntryMatcher(serverConfig.mappings, config.mappedFileResolutionRoot);
        return new CompositeEntryMatcher(Arrays.asList(mappingEntryMatcher, harEntryMatcher));
    }

    private static class VhsReplaySessionControl implements ReplaySessionControl {

        private final VirtualHarServerControl ctrl;
        private volatile boolean alive;
        private final Runnable stopListener;

        private VhsReplaySessionControl(VirtualHarServerControl ctrl, boolean alive, Runnable stopListener) {
            this.ctrl = ctrl;
            this.alive = alive;
            this.stopListener = stopListener;
        }

        @Override
        public HostAndPort getSocketAddress() {
            return ctrl.getSocketAddress();
        }

        @Override
        public int getListeningPort() {
            return ctrl.getSocketAddress().getPort();
        }

        @Override
        public boolean isAlive() {
            return alive;
        }

        @Override
        public void stop() {
            try {
                ctrl.close();
            } catch (IOException e) {
                LoggerFactory.getLogger(VhsReplaySessionControl.class).warn("close() failed", e);
            } finally {
                alive = false;
                stopListener.run();
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy