changeset 472:cdff69867843

ST-48 (host req headers), ST-49 (Date req header)
author Devel 1
date Fri, 04 Aug 2017 12:23:28 +0200
parents 34545d3acba8
children 629e5b412864
files stress-tester/src/main/java/com/passus/st/client/http/filter/HttpDateFilter.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpHostRewriterFilter.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpDateFilterTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpHostRewriterFilterTest.java
diffstat 4 files changed, 376 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpDateFilter.java	Fri Aug 04 12:23:28 2017 +0200
@@ -0,0 +1,41 @@
+package com.passus.st.client.http.filter;
+
+import com.passus.commons.annotations.Plugin;
+import com.passus.commons.time.TimeGenerator;
+import com.passus.data.ByteString;
+import com.passus.net.http.HttpDateFormatter;
+import com.passus.net.http.HttpHeaders;
+import com.passus.net.http.HttpRequest;
+import com.passus.net.http.HttpResponse;
+import com.passus.st.client.http.HttpFlowContext;
+import com.passus.st.plugin.PluginConstants;
+import java.util.Date;
+
+@Plugin(name = HttpDateFilter.TYPE, category = PluginConstants.CATEGORY_HTTP_FILTER)
+public class HttpDateFilter extends HttpFilter {
+
+    public static final String TYPE = "date";
+
+    private TimeGenerator generator = TimeGenerator.getDefaultGenerator();
+
+    void setGenerator(TimeGenerator generator) {
+        this.generator = generator;
+    }
+
+    @Override
+    public int filterOutbound(HttpRequest request, HttpResponse resp, HttpFlowContext context) {
+        HttpHeaders headers = request.getHeaders();
+        ByteString date = headers.get(HttpHeaders.DATE);
+        if (date != null) {
+            String dateString = HttpDateFormatter.format(new Date(generator.currentTimeMillis()));
+            headers.set(HttpHeaders.DATE, dateString);
+        }
+        return DUNNO;
+    }
+
+    @Override
+    public HttpFilter instanceForWorker(int index) {
+        return new HttpDateFilter();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpHostRewriterFilter.java	Fri Aug 04 12:23:28 2017 +0200
@@ -0,0 +1,172 @@
+package com.passus.st.client.http.filter;
+
+import com.passus.commons.annotations.Plugin;
+import com.passus.config.Configuration;
+import com.passus.config.annotations.NodeDefinitionCreate;
+import static com.passus.config.schema.ConfigurationSchemaBuilder.*;
+import com.passus.config.schema.MappingNodeDefinition;
+import com.passus.config.schema.NodeDefinition;
+import com.passus.config.schema.NodeDefinitionCreator;
+import com.passus.data.ByteString;
+import com.passus.data.ByteStringBuilder;
+import com.passus.data.ByteStringImpl;
+import com.passus.data.ByteStringUtils;
+import com.passus.net.http.HttpHeaders;
+import com.passus.net.http.HttpRequest;
+import com.passus.net.http.HttpResponse;
+import com.passus.net.http.URL;
+import com.passus.net.http.URLBuilder;
+import com.passus.st.client.http.HttpFlowContext;
+import com.passus.st.plugin.PluginConstants;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+@NodeDefinitionCreate(HttpHostRewriterFilter.NodeDefCreator.class)
+@Plugin(name = HttpHostRewriterFilter.TYPE, category = PluginConstants.CATEGORY_HTTP_FILTER)
+public class HttpHostRewriterFilter extends HttpFilter {
+
+    public static final String TYPE = "host";
+
+    private static final Logger LOGGER = LogManager.getLogger(HttpHostRewriterFilter.class);
+
+    private final Map<ByteString, ByteString> hostMap = new HashMap<>();
+    private final Map<HostPort, HostPort> urlMap = new HashMap<>();
+
+    public void setHostMap(Map<? extends CharSequence, ? extends CharSequence> map) {
+        hostMap.clear();
+        urlMap.clear();
+        for (Map.Entry<? extends CharSequence, ? extends CharSequence> e : map.entrySet()) {
+            ByteString key = ByteString.create(e.getKey());
+            ByteString value = ByteString.create(e.getValue());
+            hostMap.put(key, value);
+            urlMap.put(new HostPort(key), new HostPort(value));
+        }
+
+    }
+
+    @Override
+    public void configure(Configuration config) {
+        Map map = (Map) config.get("hostMap", Collections.EMPTY_MAP);
+        setHostMap(map);
+    }
+
+    @Override
+    public int filterOutbound(HttpRequest request, HttpResponse resp, HttpFlowContext context) {
+        HttpHeaders headers = request.getHeaders();
+        replaceHost(headers, HttpHeaders.HOST);
+        replaceUrl(headers, HttpHeaders.ORIGIN);
+        replaceUrl(headers, HttpHeaders.REFERER);
+        return DUNNO;
+    }
+
+    @Override
+    public HttpFilter instanceForWorker(int index) {
+        return new HttpHostRewriterFilter();
+    }
+
+    private void replaceHost(HttpHeaders headers, ByteString headerName) {
+        ByteString headerValue = headers.get(headerName);
+        ByteString mappedValue = hostMap.get(headerValue);
+        if (mappedValue != null) {
+            headers.set(headerName, mappedValue);
+        }
+    }
+
+    private void replaceUrl(HttpHeaders headers, ByteStringImpl headerName) {
+        ByteString headerValue = headers.get(headerName);
+        if (headerValue != null) {
+            try {
+                URL url = URL.parse(headerValue);
+                HostPort headerHostPort = new HostPort(url);
+                HostPort mappedHostPort = urlMap.get(headerHostPort);
+                if (mappedHostPort != null) {
+                    URLBuilder builder = new URLBuilder(url);
+                    builder.setHost(mappedHostPort.host);
+                    builder.setPort(mappedHostPort.port);
+                    URL mappedUrl = builder.build();
+                    ByteString mappedHeaderValue = mappedUrl.toByteString();
+                    headers.set(headerName, mappedHeaderValue);
+                }
+            } catch (Exception ex) {
+                LOGGER.debug("Cannot parse URL in header {}: {}", headerName, headerValue);
+            }
+        }
+    }
+
+    private static ByteStringImpl bsImpl(ByteString bs) {
+        return (bs instanceof ByteStringImpl) ? (ByteStringImpl) bs : new ByteStringImpl(bs);
+    }
+
+    static class HostPort {
+
+        final ByteString host;
+        final int port;
+
+        HostPort(URL url) {
+            this(url.getHost(), url.getPort());
+        }
+
+        HostPort(ByteString host, int port) {
+            this.host = bsImpl(host);
+            this.port = port;
+        }
+
+        HostPort(ByteString hostPortString) {
+            int lastIndex = hostPortString.lastIndexOf(":");
+            if (lastIndex >= 0) {
+                this.host = bsImpl(hostPortString.subSequence(0, lastIndex)); // workaround (equals vs SliceByteString)
+                this.port = ByteStringUtils.parseInt(hostPortString.subSequence(lastIndex + 1));
+            } else {
+                this.host = bsImpl(hostPortString); // workaround (equals vs SliceByteString)
+                this.port = -1;
+            }
+        }
+
+        public ByteString getHostPort() {
+            if (port == URL.PORT_UNSPECIFIED) {
+                return host;
+            } else {
+                ByteStringBuilder bsb = new ByteStringBuilder();
+                bsb.append(host).append(URL.PORT_SEPARATOR).append(port);
+                return bsb.toByteString();
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            return 89 * Objects.hashCode(this.host) + this.port;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (!(obj instanceof HostPort)) {
+                return false;
+            }
+            final HostPort other = (HostPort) obj;
+            return this.port == other.port && Objects.equals(this.host, other.host);
+        }
+
+        @Override
+        public String toString() {
+            return "HostPort{" + hashCode() + "=" + getHostPort() + '}';
+        }
+    }
+
+    public static class NodeDefCreator implements NodeDefinitionCreator {
+
+        @Override
+        public NodeDefinition create() {
+            return mapDef(
+                    tupleDef("hostMap", new MappingNodeDefinition())
+            );
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpDateFilterTest.java	Fri Aug 04 12:23:28 2017 +0200
@@ -0,0 +1,29 @@
+package com.passus.st.client.http.filter;
+
+import com.passus.commons.time.CustomTimeGenerator;
+import com.passus.net.http.HttpHeaders;
+import com.passus.net.http.HttpRequest;
+import com.passus.net.http.HttpRequestBuilder;
+import static org.testng.Assert.*;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author mikolaj.podbielski
+ */
+public class HttpDateFilterTest {
+
+    @Test
+    public void testFilterOutbound() {
+        HttpRequest req = HttpRequestBuilder.get("http://example.com/")
+                .header(HttpHeaders.DATE, "Wed, 09-Sep-2009 09:09:00 GMT")
+                .build();
+
+        HttpDateFilter filter = new HttpDateFilter();
+        filter.setGenerator(new CustomTimeGenerator(1500_000_000_000L));
+
+        filter.filterOutbound(req, null, null);
+        assertEquals(req.getHeaders().get(HttpHeaders.DATE).toString(), "Fri, 14 Jul 2017 02:40:00 GMT");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpHostRewriterFilterTest.java	Fri Aug 04 12:23:28 2017 +0200
@@ -0,0 +1,134 @@
+package com.passus.st.client.http.filter;
+
+import static com.passus.commons.collection.FluentBuilder.*;
+import com.passus.config.validation.Errors;
+import com.passus.data.ByteString;
+import com.passus.net.http.HttpHeaders;
+import static com.passus.net.http.HttpHeaders.*;
+import com.passus.net.http.HttpRequest;
+import com.passus.net.http.HttpRequestBuilder;
+import com.passus.st.client.http.filter.HttpHostRewriterFilter.HostPort;
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.Map;
+import static org.testng.AssertJUnit.*;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author mikolaj.podbielski
+ */
+public class HttpHostRewriterFilterTest {
+
+    @Test
+    public void testFilterOutbound() {
+        Map<String, String> hostMap = map(
+                e("example.com", "mapped.com:8088"),
+                e("example.com:8080", "mapped.com")
+        );
+        HttpHostRewriterFilter filter = new HttpHostRewriterFilter();
+        filter.setHostMap(hostMap);
+
+        HttpRequest req0 = request("http://wp.pl/site1/resource1", "http://wp.pl/site1", "http://wp.pl/site1/index");
+        filter.filterOutbound(req0, null, null);
+        assertHeaderValues(req0, "wp.pl", "http://wp.pl/site1", "http://wp.pl/site1/index");
+
+        HttpRequest req1 = request("http://example.com/site1/resource1", "http://example.com/site1", "http://example.com/site1/index");
+        filter.filterOutbound(req1, null, null);
+        assertHeaderValues(req1, "mapped.com:8088", "http://mapped.com:8088/site1", "http://mapped.com:8088/site1/index");
+
+        HttpRequest req2 = request("http://example.com:8080/site1/resource1", "http://example.com:8080/site1", "http://example.com:8080/site1/index");
+        filter.filterOutbound(req2, null, null);
+        assertHeaderValues(req2, "mapped.com", "http://mapped.com/site1", "http://mapped.com/site1/index");
+    }
+
+    private static HttpRequest request(String url, String origin, String referer) {
+        return HttpRequestBuilder.get(url).header(ORIGIN, origin).header(REFERER, referer).build();
+    }
+
+    private static void assertHeaderValues(HttpRequest req, String host, String origin, String referer) {
+        HttpHeaders headers = req.getHeaders();
+        assertEquals(headers.get(HOST).toString(), host);
+        assertEquals(headers.get(ORIGIN).toString(), origin);
+        assertEquals(headers.get(REFERER).toString(), referer);
+    }
+
+    @Test
+    public void testHostPort() {
+        assertEquals(new HostPort(bs("abc.def"), -1).getHostPort().toString(), "abc.def");
+        assertEquals(new HostPort(bs("abc.def"), 80).getHostPort().toString(), "abc.def:80");
+
+        HostPort hp1 = new HostPort(bs("abc.def"));
+        assertEquals(hp1.host.toString(), "abc.def");
+        assertEquals(hp1.port, -1);
+        HostPort hp2 = new HostPort(bs("abc.def:80"));
+        assertEquals(hp2.host.toString(), "abc.def");
+        assertEquals(hp2.port, 80);
+
+        try {
+            HostPort hp = new HostPort(bs("abc:"));
+            fail();
+        } catch (Exception ignore) {
+        }
+        try {
+            HostPort hp = new HostPort(bs(":"));
+            fail();
+        } catch (Exception ignore) {
+        }
+        HostPort hp = new HostPort(bs(":80"));
+        assertEquals(hp.host.toString(), "");
+        assertEquals(hp.port, 80);
+    }
+
+    private static final Field hostMapField;
+    private static final Field urlMapField;
+
+    static {
+        try {
+            hostMapField = HttpHostRewriterFilter.class.getDeclaredField("hostMap");
+            hostMapField.setAccessible(true);
+            urlMapField = HttpHostRewriterFilter.class.getDeclaredField("urlMap");
+            urlMapField.setAccessible(true);
+        } catch (Exception ex) {
+            throw new ExceptionInInitializerError(ex);
+        }
+    }
+
+    @Test
+    public void testConfigure() throws Exception {
+        String filterConfig = "filters:\n"
+                + "    - type: host\n"
+                + "      hostMap:\n"
+                + "        \"example.com\": \"mapped.com:8088\"\n"
+                + "        \"example.com:8080\": \"mapped.com\"\n";
+
+        Errors errors = new Errors();
+        List<HttpFilter> filters = HttpFiltersConfigurator.getFilters(filterConfig, errors);
+
+        assertEquals(0, errors.getErrorCount());
+        assertEquals(1, filters.size());
+        assertTrue(filters.get(0) instanceof HttpHostRewriterFilter);
+
+        HttpHostRewriterFilter filter = (HttpHostRewriterFilter) filters.get(0);
+
+        Map<ByteString, ByteString> expectedHostMap = map(
+                e(bs("example.com"), bs("mapped.com:8088")),
+                e(bs("example.com:8080"), bs("mapped.com"))
+        );
+        Map<HostPort, HostPort> expectedUrlMap = map(
+                e(hp("example.com", -1), hp("mapped.com", 8088)),
+                e(hp("example.com", 8080), hp("mapped.com", -1))
+        );
+
+        assertEquals(expectedHostMap, hostMapField.get(filter));
+        assertEquals(expectedUrlMap, urlMapField.get(filter));
+    }
+
+    private static ByteString bs(CharSequence cs) {
+        return ByteString.create(cs);
+    }
+
+    private static HostPort hp(String h, int p) {
+        return new HostPort(bs(h), p);
+    }
+}