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

net.lightbody.bmp.proxy.bricks.ProxyResource Maven / Gradle / Ivy

There is a newer version: 2.1.5
Show newest version
package net.lightbody.bmp.proxy.bricks;

import com.google.inject.Inject;
import com.google.inject.name.Named;
import com.google.sitebricks.At;
import com.google.sitebricks.client.transport.Json;
import com.google.sitebricks.client.transport.Text;
import com.google.sitebricks.headless.Reply;
import com.google.sitebricks.headless.Request;
import com.google.sitebricks.headless.Service;
import com.google.sitebricks.http.Delete;
import com.google.sitebricks.http.Get;
import com.google.sitebricks.http.Post;
import com.google.sitebricks.http.Put;
import net.lightbody.bmp.BrowserMobProxy;
import net.lightbody.bmp.BrowserMobProxyServer;
import net.lightbody.bmp.core.har.Har;
import net.lightbody.bmp.exception.ProxyExistsException;
import net.lightbody.bmp.exception.ProxyPortsExhaustedException;
import net.lightbody.bmp.exception.UnsupportedCharsetException;
import net.lightbody.bmp.filters.JavascriptRequestResponseFilter;
import net.lightbody.bmp.proxy.LegacyProxyServer;
import net.lightbody.bmp.proxy.ProxyManager;
import net.lightbody.bmp.proxy.ProxyServer;
import net.lightbody.bmp.proxy.http.BrowserMobHttpRequest;
import net.lightbody.bmp.proxy.http.BrowserMobHttpResponse;
import net.lightbody.bmp.proxy.http.RequestInterceptor;
import net.lightbody.bmp.proxy.http.ResponseInterceptor;
import net.lightbody.bmp.util.BrowserMobHttpUtil;
import org.java_bandwidthlimiter.StreamManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

@At("/proxy")
@Service
public class ProxyResource {
    private static final Logger LOG = LoggerFactory.getLogger(ProxyResource.class);

    private final ProxyManager proxyManager;

    @Inject
    public ProxyResource(ProxyManager proxyManager) {
        this.proxyManager = proxyManager;
    }

    @Get
    public Reply getProxies() {
        Collection proxyList = new ArrayList();
        for (LegacyProxyServer proxy : proxyManager.get()) {
            proxyList.add(new ProxyDescriptor(proxy.getPort()));
        }
        return Reply.with(new ProxyListDescriptor(proxyList)).as(Json.class);
    }

    @Post
    public Reply newProxy(Request request) {
        String systemProxyHost = System.getProperty("http.proxyHost");
        String systemProxyPort = System.getProperty("http.proxyPort");
        String httpProxy = request.param("httpProxy");
        String proxyUsername = request.param("proxyUsername");
        String proxyPassword = request.param("proxyPassword");

        Hashtable options = new Hashtable();

        // If the upstream proxy is specified via query params that should override any default system level proxy.
        if (httpProxy != null) {
            options.put("httpProxy", httpProxy);
        } else if ((systemProxyHost != null) && (systemProxyPort != null)) {
            options.put("httpProxy", String.format("%s:%s", systemProxyHost, systemProxyPort));
        }

        // this is a short-term work-around for Proxy Auth in the REST API until the upcoming REST API refactor
        if (proxyUsername != null && proxyPassword != null) {
            options.put("proxyUsername", proxyUsername);
            options.put("proxyPassword", proxyPassword);
        }

        String paramBindAddr = request.param("bindAddress");
        Integer paramPort = request.param("port") == null ? null : Integer.parseInt(request.param("port"));

        String useEccString = request.param("useEcc");
        boolean useEcc = Boolean.parseBoolean(useEccString);

        String trustAllServersString = request.param("trustAllServers");
        boolean trustAllServers = Boolean.parseBoolean(trustAllServersString);

        LOG.debug("POST proxy instance on bindAddress `{}` & port `{}`",
                paramBindAddr, paramPort);
        LegacyProxyServer proxy;
        try {
            proxy = proxyManager.create(options, paramPort, paramBindAddr, useEcc, trustAllServers);
        } catch (ProxyExistsException ex) {
            return Reply.with(new ProxyDescriptor(ex.getPort())).status(455).as(Json.class);
        } catch (ProxyPortsExhaustedException ex) {
            return Reply.saying().status(456);
        } catch (Exception ex) {
            StringWriter s = new StringWriter();
            ex.printStackTrace(new PrintWriter(s));
            return Reply.with(s).as(Text.class).status(550);
        }
        return Reply.with(new ProxyDescriptor(proxy.getPort())).as(Json.class);
    }

    @Get
    @At("/:port/har")
    public Reply getHar(@Named("port") int port) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        Har har = proxy.getHar();

        return Reply.with(har).as(Json.class);
    }

    @Put
    @At("/:port/har")
    public Reply newHar(@Named("port") int port, Request request) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        String initialPageRef = request.param("initialPageRef");
        String initialPageTitle = request.param("initialPageTitle");
        Har oldHar = proxy.newHar(initialPageRef, initialPageTitle);

        String captureHeaders = request.param("captureHeaders");
        String captureContent = request.param("captureContent");
        String captureBinaryContent = request.param("captureBinaryContent");
        proxy.setCaptureHeaders(Boolean.parseBoolean(captureHeaders));
        proxy.setCaptureContent(Boolean.parseBoolean(captureContent));
        proxy.setCaptureBinaryContent(Boolean.parseBoolean(captureBinaryContent));

        if (oldHar != null) {
            return Reply.with(oldHar).as(Json.class);
        } else {
            return Reply.saying().noContent();
        }
    }

    @Put
    @At("/:port/har/pageRef")
    public Reply setPage(@Named("port") int port, Request request) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        String pageRef = request.param("pageRef");
        String pageTitle = request.param("pageTitle");
        proxy.newPage(pageRef, pageTitle);

        return Reply.saying().ok();
    }

    @Get
    @At("/:port/blacklist")
    public Reply getBlacklist(@Named("port") int port, Request request) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        return Reply.with(proxy.getBlacklistedUrls()).as(Json.class);
    }

    @Put
    @At("/:port/blacklist")
    public Reply blacklist(@Named("port") int port, Request request) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        String blacklist = request.param("regex");
        int responseCode = parseResponseCode(request.param("status"));
        String method = request.param("method");
        proxy.blacklistRequests(blacklist, responseCode, method);

        return Reply.saying().ok();
    }

    @Delete
    @At("/:port/blacklist")
    public Reply clearBlacklist(@Named("port") int port, Request request) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        proxy.clearBlacklist();
        return Reply.saying().ok();
    }

    @Get
    @At("/:port/whitelist")
    public Reply getWhitelist(@Named("port") int port, Request request) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        return Reply.with(proxy.getWhitelistUrls()).as(Json.class);
    }

    @Put
    @At("/:port/whitelist")
    public Reply whitelist(@Named("port") int port, Request request) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        String regex = request.param("regex");
        int responseCode = parseResponseCode(request.param("status"));
        proxy.whitelistRequests(regex.split(","), responseCode);

        return Reply.saying().ok();
    }

    @Delete
    @At("/:port/whitelist")
    public Reply clearWhitelist(@Named("port") int port, Request request) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        proxy.clearWhitelist();
        return Reply.saying().ok();
    }

    @Post
    @At("/:port/auth/basic/:domain")
    public Reply autoBasicAuth(@Named("port") int port, @Named("domain") String domain, Request request) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        Map credentials = request.read(HashMap.class).as(Json.class);
        proxy.autoBasicAuthorization(domain, credentials.get("username"), credentials.get("password"));

        return Reply.saying().ok();
    }

    @Post
    @At("/:port/headers")
    public Reply updateHeaders(@Named("port") int port, Request request) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        Map headers = request.read(Map.class).as(Json.class);
        for (Map.Entry entry : headers.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            proxy.addHeader(key, value);
        }
        return Reply.saying().ok();
    }

    @Post
    @At("/:port/interceptor/response")
    public Reply addResponseInterceptor(@Named("port") int port, Request request) throws IOException, ScriptException {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        if (!(proxy instanceof ProxyServer)) {
            return Reply.saying().badRequest();
        }

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        request.readTo(baos);

        ScriptEngineManager mgr = new ScriptEngineManager();
        final ScriptEngine engine = mgr.getEngineByName("JavaScript");
        Compilable compilable = (Compilable) engine;
        final CompiledScript script = compilable.compile(baos.toString());

        proxy.addResponseInterceptor(new ResponseInterceptor() {
            @Override
            public void process(BrowserMobHttpResponse response, Har har) {
                Bindings bindings = engine.createBindings();
                bindings.put("response", response);
                bindings.put("har", har);
                bindings.put("log", LOG);
                try {
                    script.eval(bindings);
                } catch (ScriptException e) {
                    LOG.error("Could not execute JS-based response interceptor", e);
                }
            }
        });

        return Reply.saying().ok();
    }

    @Post
    @At("/:port/interceptor/request")
    public Reply addRequestInterceptor(@Named("port") int port, Request request) throws IOException, ScriptException {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        if (!(proxy instanceof ProxyServer)) {
            return Reply.saying().badRequest();
        }

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        request.readTo(baos);

        ScriptEngineManager mgr = new ScriptEngineManager();
        final ScriptEngine engine = mgr.getEngineByName("JavaScript");
        Compilable compilable = (Compilable) engine;
        final CompiledScript script = compilable.compile(baos.toString());

        proxy.addRequestInterceptor(new RequestInterceptor() {
            @Override
            public void process(BrowserMobHttpRequest request, Har har) {
                Bindings bindings = engine.createBindings();
                bindings.put("request", request);
                bindings.put("har", har);
                bindings.put("log", LOG);
                try {
                    script.eval(bindings);
                } catch (ScriptException e) {
                    LOG.error("Could not execute JS-based response interceptor", e);
                }
            }
        });

        return Reply.saying().ok();
    }

    @Post
    @At("/:port/filter/request")
    public Reply addRequestFilter(@Named("port") int port, Request request) throws IOException, ScriptException {
        LegacyProxyServer legacyProxy = proxyManager.get(port);
        if (legacyProxy == null) {
            return Reply.saying().notFound();
        }

        if (!(legacyProxy instanceof BrowserMobProxyServer)) {
            return Reply.saying().badRequest();
        }

        BrowserMobProxy proxy = (BrowserMobProxy) legacyProxy;

        JavascriptRequestResponseFilter requestResponseFilter = new JavascriptRequestResponseFilter();

        String script = getEntityBodyFromRequest(request);
        requestResponseFilter.setRequestFilterScript(script);

        proxy.addRequestFilter(requestResponseFilter);

        return Reply.saying().ok();
    }

    @Post
    @At("/:port/filter/response")
    public Reply addResponseFilter(@Named("port") int port, Request request) throws IOException, ScriptException {
        LegacyProxyServer legacyProxy = proxyManager.get(port);
        if (legacyProxy == null) {
            return Reply.saying().notFound();
        }

        if (!(legacyProxy instanceof BrowserMobProxyServer)) {
            return Reply.saying().badRequest();
        }

        BrowserMobProxy proxy = (BrowserMobProxy) legacyProxy;

        JavascriptRequestResponseFilter requestResponseFilter = new JavascriptRequestResponseFilter();

        String script = getEntityBodyFromRequest(request);
        requestResponseFilter.setResponseFilterScript(script);

        proxy.addResponseFilter(requestResponseFilter);

        return Reply.saying().ok();
    }

    @Put
    @At("/:port/limit")
    public Reply limit(@Named("port") int port, Request request) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        StreamManager streamManager = proxy.getStreamManager();
        String upstreamKbps = request.param("upstreamKbps");
        if (upstreamKbps != null) {
            try {
                streamManager.setUpstreamKbps(Integer.parseInt(upstreamKbps));
                streamManager.enable();
            } catch (NumberFormatException e) {
            }
        }

        String upstreamBps = request.param("upstreamBps");
        if (upstreamBps != null) {
            try {
                ((BrowserMobProxy) proxy).setWriteBandwidthLimit(Integer.parseInt(upstreamBps));
            } catch (NumberFormatException e) {
            }
        }

        String downstreamKbps = request.param("downstreamKbps");
        if (downstreamKbps != null) {
            try {
                streamManager.setDownstreamKbps(Integer.parseInt(downstreamKbps));
                streamManager.enable();
            } catch (NumberFormatException e) {
            }
        }

        String downstreamBps = request.param("downstreamBps");
        if (downstreamBps != null) {
            try {
                ((BrowserMobProxy) proxy).setReadBandwidthLimit(Integer.parseInt(downstreamBps));
            } catch (NumberFormatException e) {
            }
        }

        String upstreamMaxKB = request.param("upstreamMaxKB");
        if (upstreamMaxKB != null) {
            try {
                streamManager.setUpstreamMaxKB(Integer.parseInt(upstreamMaxKB));
                streamManager.enable();
            } catch (NumberFormatException e) {
            }
        }
        String downstreamMaxKB = request.param("downstreamMaxKB");
        if (downstreamMaxKB != null) {
            try {
                streamManager.setDownstreamMaxKB(Integer.parseInt(downstreamMaxKB));
                streamManager.enable();
            } catch (NumberFormatException e) {
            }
        }
        String latency = request.param("latency");
        if (latency != null) {
            try {
                streamManager.setLatency(Integer.parseInt(latency));
                streamManager.enable();
            } catch (NumberFormatException e) {
            }
        }
        String payloadPercentage = request.param("payloadPercentage");
        if (payloadPercentage != null) {
            try {
                streamManager.setPayloadPercentage(Integer.parseInt(payloadPercentage));
            } catch (NumberFormatException e) {
            }
        }
        String maxBitsPerSecond = request.param("maxBitsPerSecond");
        if (maxBitsPerSecond != null) {
            try {
                streamManager.setMaxBitsPerSecondThreshold(Integer.parseInt(maxBitsPerSecond));
            } catch (NumberFormatException e) {
            }
        }
        String enable = request.param("enable");
        if (enable != null) {
            if (Boolean.parseBoolean(enable)) {
                streamManager.enable();
            } else {
                streamManager.disable();
            }
        }
        return Reply.saying().ok();
    }

    @Get
    @At("/:port/limit")
    public Reply getLimits(@Named("port") int port, Request request) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }
        return Reply.with(new BandwidthLimitDescriptor(proxy.getStreamManager())).as(Json.class);
    }

    @Put
    @At("/:port/timeout")
    public Reply timeout(@Named("port") int port, Request request) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        String requestTimeout = request.param("requestTimeout");
        if (requestTimeout != null) {
            try {
                proxy.setRequestTimeout(Integer.parseInt(requestTimeout));
            } catch (NumberFormatException e) {
            }
        }
        String readTimeout = request.param("readTimeout");
        if (readTimeout != null) {
            try {
                proxy.setSocketOperationTimeout(Integer.parseInt(readTimeout));
            } catch (NumberFormatException e) {
            }
        }
        String connectionTimeout = request.param("connectionTimeout");
        if (connectionTimeout != null) {
            try {
                proxy.setConnectionTimeout(Integer.parseInt(connectionTimeout));
            } catch (NumberFormatException e) {
            }
        }
        String dnsCacheTimeout = request.param("dnsCacheTimeout");
        if (dnsCacheTimeout != null) {
            try {
                proxy.setDNSCacheTimeout(Integer.parseInt(dnsCacheTimeout));
            } catch (NumberFormatException e) {
            }
        }
        return Reply.saying().ok();
    }

    @Delete
    @At("/:port")
    public Reply delete(@Named("port") int port) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        proxyManager.delete(port);
        return Reply.saying().ok();
    }

    @Post
    @At("/:port/hosts")
    public Reply remapHosts(@Named("port") int port, Request request) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        @SuppressWarnings("unchecked") Map headers = request.read(Map.class).as(Json.class);

        for (Map.Entry entry : headers.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            proxy.remapHost(key, value);
            proxy.setDNSCacheTimeout(0);
            proxy.clearDNSCache();
        }

        return Reply.saying().ok();
    }


    @Put
    @At("/:port/wait")
    public Reply wait(@Named("port") int port, Request request) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        String quietPeriodInMs = request.param("quietPeriodInMs");
        String timeoutInMs = request.param("timeoutInMs");
        proxy.waitForNetworkTrafficToStop(Integer.parseInt(quietPeriodInMs), Integer.parseInt(timeoutInMs));
        return Reply.saying().ok();
    }

    @Delete
    @At("/:port/dns/cache")
    public Reply clearDnsCache(@Named("port") int port) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        proxy.clearDNSCache();
        return Reply.saying().ok();
    }

    @Put
    @At("/:port/rewrite")
    public Reply rewriteUrl(@Named("port") int port, Request request) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        String match = request.param("matchRegex");
        String replace = request.param("replace");
        proxy.rewriteUrl(match, replace);
        return Reply.saying().ok();
    }

    @Delete
    @At("/:port/rewrite")
    public Reply clearRewriteRules(@Named("port") int port, Request request) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        proxy.clearRewriteRules();
        return Reply.saying().ok();
    }

    @Put
    @At("/:port/retry")
    public Reply retryCount(@Named("port") int port, Request request) {
        LegacyProxyServer proxy = proxyManager.get(port);
        if (proxy == null) {
            return Reply.saying().notFound();
        }

        String count = request.param("retrycount");
        proxy.setRetryCount(Integer.parseInt(count));
        return Reply.saying().ok();
    }

    private int parseResponseCode(String response) {
        int responseCode = 200;
        if (response != null) {
            try {
                responseCode = Integer.parseInt(response);
            } catch (NumberFormatException e) {
            }
        }
        return responseCode;
    }

    public static class ProxyDescriptor {
        private int port;

        public ProxyDescriptor() {
        }

        public ProxyDescriptor(int port) {
            this.port = port;
        }

        public int getPort() {
            return port;
        }

        public void setPort(int port) {
            this.port = port;
        }
    }

    public static class ProxyListDescriptor {
        private Collection proxyList;

        public ProxyListDescriptor() {
        }

        public ProxyListDescriptor(Collection proxyList) {
            this.proxyList = proxyList;
        }

        public Collection getProxyList() {
            return proxyList;
        }

        public void setProxyList(Collection proxyList) {
            this.proxyList = proxyList;
        }
    }

    public static class BandwidthLimitDescriptor {
        private long maxUpstreamKB;
        private long remainingUpstreamKB;
        private long maxDownstreamKB;
        private long remainingDownstreamKB;

        public BandwidthLimitDescriptor() {
        }

        public BandwidthLimitDescriptor(StreamManager manager) {
            this.maxDownstreamKB = manager.getMaxDownstreamKB();
            this.remainingDownstreamKB = manager.getRemainingDownstreamKB();
            this.maxUpstreamKB = manager.getMaxUpstreamKB();
            this.remainingUpstreamKB = manager.getRemainingUpstreamKB();
        }

        public long getMaxUpstreamKB() {
            return maxUpstreamKB;
        }

        public void setMaxUpstreamKB(long maxUpstreamKB) {
            this.maxUpstreamKB = maxUpstreamKB;
        }

        public long getRemainingUpstreamKB() {
            return remainingUpstreamKB;
        }

        public void setRemainingUpstreamKB(long remainingUpstreamKB) {
            this.remainingUpstreamKB = remainingUpstreamKB;
        }

        public long getMaxDownstreamKB() {
            return maxDownstreamKB;
        }

        public void setMaxDownstreamKB(long maxDownstreamKB) {
            this.maxDownstreamKB = maxDownstreamKB;
        }

        public long getRemainingDownstreamKB() {
            return remainingDownstreamKB;
        }

        public void setRemainingDownstreamKB(long remainingDownstreamKB) {
            this.remainingDownstreamKB = remainingDownstreamKB;
        }
    }

    private String getEntityBodyFromRequest(Request request) throws IOException {
        String contentTypeHeader = request.header("Content-Type");
        Charset charset = null;
        try {
            charset = BrowserMobHttpUtil.readCharsetInContentTypeHeader(contentTypeHeader);
        } catch (UnsupportedCharsetException e) {
            java.nio.charset.UnsupportedCharsetException cause = e.getUnsupportedCharsetExceptionCause();
            LOG.error("Character set declared in Content-Type header is not supported. Content-Type header: {}", contentTypeHeader, cause);

            throw cause;
        }

        if (charset == null) {
            charset = BrowserMobHttpUtil.DEFAULT_HTTP_CHARSET;
        }

        ByteArrayOutputStream entityBodyBytes = new ByteArrayOutputStream();
        request.readTo(entityBodyBytes);

        return new String(entityBodyBytes.toByteArray(), charset);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy