changeset 736:967ac5f48f58

ContentExtractor, HttpMessageModificationFilter "$setContent" operation
author Devel 2
date Tue, 05 Dec 2017 11:07:07 +0100
parents 843e6848a49b
children c9027685888e
files stress-tester/src/main/java/com/passus/st/client/http/extractor/ContentExtractor.java stress-tester/src/main/java/com/passus/st/client/http/extractor/ContentExtractorUtils.java stress-tester/src/main/java/com/passus/st/client/http/extractor/ContentReplacer.java stress-tester/src/main/java/com/passus/st/client/http/extractor/JsonValueExtractor.java stress-tester/src/main/java/com/passus/st/client/http/extractor/PostValueExtractor.java stress-tester/src/main/java/com/passus/st/client/http/extractor/RegexValueExtractor.java stress-tester/src/main/java/com/passus/st/client/http/extractor/XmlValueExtractor.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageModificationFilter.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTransformer.java stress-tester/src/main/java/com/passus/st/client/http/filter/HttpResponseContentExtractorFilter.java stress-tester/src/main/java/com/passus/st/filter/HttpMessageHeaderValueExtractor.java stress-tester/src/test/java/com/passus/st/client/http/extractor/ContentExtractorTransformerTest.java stress-tester/src/test/java/com/passus/st/client/http/extractor/JsonValueExtractorTest.java stress-tester/src/test/java/com/passus/st/client/http/extractor/PostValueExtractorTest.java stress-tester/src/test/java/com/passus/st/client/http/extractor/RegexValueExtractorTest.java stress-tester/src/test/java/com/passus/st/client/http/extractor/XmlValueExtractorTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTransformerTest.java stress-tester/src/test/java/com/passus/st/client/http/filter/HttpResponseContentExtractorFilterTest.java
diffstat 19 files changed, 307 insertions(+), 90 deletions(-) [+]
line wrap: on
line diff
--- a/stress-tester/src/main/java/com/passus/st/client/http/extractor/ContentExtractor.java	Tue Dec 05 09:36:17 2017 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/extractor/ContentExtractor.java	Tue Dec 05 11:07:07 2017 +0100
@@ -10,10 +10,10 @@
  */
 public interface ContentExtractor {
 
-    public default String extract(HttpMessage message) throws IOException {
+    public default CharSequence extract(HttpMessage message) throws IOException {
         String content = HttpMessageHelper.get().contentToString(message, true);
         return extract(content);
     }
 
-    public String extract(String content) throws IOException;
+    public CharSequence extract(CharSequence content) throws IOException;
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/client/http/extractor/ContentExtractorUtils.java	Tue Dec 05 11:07:07 2017 +0100
@@ -0,0 +1,100 @@
+package com.passus.st.client.http.extractor;
+
+import com.passus.commons.Assert;
+
+/**
+ *
+ * @author Mirosław Hawrot
+ */
+public class ContentExtractorUtils {
+
+    private ContentExtractorUtils() {
+    }
+
+    public static ContentReplacer createReplacer(String rule) {
+        Assert.notNull(rule, "rule");
+        if (rule.length() < 4) {
+            throw new IllegalArgumentException("Invalid replacer rule '" + rule + "'.");
+        }
+
+        int pos = rule.indexOf(':');
+        if (pos == -1) {
+            throw new IllegalArgumentException("Invalid replacer rule '" + rule + "'.");
+        }
+
+        ContentReplacer contentReplacer = null;
+        try {
+            String extractorType = rule.substring(0, pos);
+            String expr = rule.substring(pos + 1);
+
+            switch (extractorType.toLowerCase()) {
+                case "json":
+                    contentReplacer = new JsonValueExtractor(expr);
+                    break;
+                case "xml":
+                    contentReplacer = new XmlValueExtractor(expr);
+                    break;
+                case "post":
+                    contentReplacer = new PostValueExtractor(expr);
+                    break;
+                case "regexp":
+                    contentReplacer = new RegexValueExtractor(expr);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Invalid replacer rule '" + rule + "'. Invalid replacer type '" + extractorType + "'.");
+            }
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Invalid replacer rule '" + rule + "'." + e.getMessage(), e);
+        }
+
+        if (contentReplacer == null) {
+            throw new IllegalArgumentException("Invalid replacer rule '" + rule + "'.");
+        }
+
+        return contentReplacer;
+    }
+
+    public static ContentExtractor createExtractor(String rule) {
+        Assert.notNull(rule, "rule");
+        if (rule.length() < 4) {
+            throw new IllegalArgumentException("Invalid extractor rule '" + rule + "'.");
+        }
+
+        int pos = rule.indexOf(':');
+        if (pos == -1) {
+            throw new IllegalArgumentException("Invalid extractor rule '" + rule + "'.");
+        }
+
+        ContentExtractor contentExtractor = null;
+        try {
+            String extractorType = rule.substring(0, pos);
+            String expr = rule.substring(pos + 1);
+
+            switch (extractorType.toLowerCase()) {
+                case "json":
+                    contentExtractor = new JsonValueExtractor(expr);
+                    break;
+                case "xml":
+                    contentExtractor = new XmlValueExtractor(expr);
+                    break;
+                case "post":
+                    contentExtractor = new PostValueExtractor(expr);
+                    break;
+                case "regexp":
+                    contentExtractor = new RegexValueExtractor(expr);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Invalid extractor rule '" + rule + "'. Invalid extractor type '" + extractorType + "'.");
+            }
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Invalid extractor rule '" + rule + "'." + e.getMessage(), e);
+        }
+
+        if (contentExtractor == null) {
+            throw new IllegalArgumentException("Invalid extractor rule '" + rule + "'.");
+        }
+
+        return contentExtractor;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/stress-tester/src/main/java/com/passus/st/client/http/extractor/ContentReplacer.java	Tue Dec 05 11:07:07 2017 +0100
@@ -0,0 +1,24 @@
+package com.passus.st.client.http.extractor;
+
+import com.passus.net.http.HttpMessage;
+import com.passus.net.http.HttpMessageHelper;
+import java.io.IOException;
+
+/**
+ *
+ * @author Mirosław Hawrot
+ */
+public interface ContentReplacer {
+
+    public default void replace(HttpMessage message, CharSequence value) throws IOException {
+        HttpMessageHelper helper = HttpMessageHelper.get();
+        CharSequence content = helper.contentToString(message, true);
+        if (content != null) {
+            CharSequence newContent = replace(content, value);
+            helper.setContent(message, newContent);
+        }
+    }
+
+    public CharSequence replace(CharSequence content, CharSequence value) throws IOException;
+
+}
--- a/stress-tester/src/main/java/com/passus/st/client/http/extractor/JsonValueExtractor.java	Tue Dec 05 09:36:17 2017 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/extractor/JsonValueExtractor.java	Tue Dec 05 11:07:07 2017 +0100
@@ -4,6 +4,9 @@
 import com.jayway.jsonpath.DocumentContext;
 import com.jayway.jsonpath.JsonPath;
 import static com.jayway.jsonpath.Option.DEFAULT_PATH_LEAF_TO_NULL;
+import com.passus.net.http.HttpMessage;
+import com.passus.net.http.HttpMessageHelper;
+import java.io.IOException;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
 
@@ -11,7 +14,7 @@
  *
  * @author mikolaj.podbielski
  */
-public class JsonValueExtractor implements ContentExtractor {
+public class JsonValueExtractor implements ContentExtractor, ContentReplacer {
 
     private static final Logger LOGGER = LogManager.getLogger(JsonValueExtractor.class);
 
@@ -35,9 +38,10 @@
         }
     }
 
-    public String replace(String json, String value) {
+    @Override
+    public CharSequence replace(CharSequence content, CharSequence value) {
         try {
-            DocumentContext context = parse(json);
+            DocumentContext context = parse(content.toString());
             if (context == null) {
                 return null;
             }
@@ -55,9 +59,9 @@
     }
 
     @Override
-    public String extract(String json) {
+    public CharSequence extract(CharSequence content) {
         try {
-            DocumentContext context = parse(json);
+            DocumentContext context = parse(content.toString());
             if (context == null) {
                 return null;
             }
@@ -72,4 +76,5 @@
             return null;
         }
     }
+
 }
--- a/stress-tester/src/main/java/com/passus/st/client/http/extractor/PostValueExtractor.java	Tue Dec 05 09:36:17 2017 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/extractor/PostValueExtractor.java	Tue Dec 05 11:07:07 2017 +0100
@@ -11,7 +11,7 @@
  *
  * @author Mirosław Hawrot
  */
-public class PostValueExtractor implements ContentExtractor {
+public class PostValueExtractor implements ContentExtractor, ContentReplacer {
 
     protected final HttpMessageHelper helper = HttpMessageHelper.get();
 
@@ -22,7 +22,17 @@
         this.fieldName = ByteString.create(fieldName);
     }
 
-    public String replace(String content, String value) throws IOException {
+    @Override
+    public void replace(HttpMessage message, CharSequence value) throws IOException {
+        HttpParameters params = helper.decodeFormUrlencoded(message);
+        if (params != null) {
+            params.set(fieldName, value);
+            helper.setFormUrlencoded(message, params);
+        }
+    }
+
+    @Override
+    public CharSequence replace(CharSequence content, CharSequence value) throws IOException {
         HttpParameters params = helper.decodeFormUrlencoded(content);
         if (params != null) {
             params.set(fieldName, value);
@@ -33,12 +43,12 @@
     }
 
     @Override
-    public String extract(HttpMessage message) throws IOException {
+    public CharSequence extract(HttpMessage message) throws IOException {
         HttpParameters params = helper.decodeFormUrlencoded(message);
         if (params != null) {
             ByteString value = params.get(fieldName);
             if (value != null) {
-                return value.toString();
+                return value;
             }
         }
 
@@ -46,12 +56,12 @@
     }
 
     @Override
-    public String extract(String content) throws IOException {
+    public CharSequence extract(CharSequence content) throws IOException {
         HttpParameters params = helper.decodeFormUrlencoded(content);
         if (params != null) {
             ByteString value = params.get(fieldName);
             if (value != null) {
-                return value.toString();
+                return value;
             }
         }
 
--- a/stress-tester/src/main/java/com/passus/st/client/http/extractor/RegexValueExtractor.java	Tue Dec 05 09:36:17 2017 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/extractor/RegexValueExtractor.java	Tue Dec 05 11:07:07 2017 +0100
@@ -7,18 +7,19 @@
  *
  * @author mikolaj.podbielski
  */
-public class RegexValueExtractor implements ContentExtractor {
+public class RegexValueExtractor implements ContentExtractor, ContentReplacer {
 
     private final Pattern pattern;
 
     private final StringBuilder sb = new StringBuilder();
 
-    public RegexValueExtractor(String expression) {
-        pattern = Pattern.compile(expression);
+    public RegexValueExtractor(CharSequence expression) {
+        pattern = Pattern.compile(expression.toString());
     }
 
-    public String replace(String object, String value) {
-        if (object == null || object.isEmpty()) {
+    @Override
+    public CharSequence replace(CharSequence object, CharSequence value) {
+        if (object == null || object.length() == 0) {
             return object;
         }
 
@@ -30,7 +31,7 @@
                 if (start != -1 && end != -1) {
                     sb.setLength(0);
                     sb.append(object);
-                    sb.replace(start, end, value);
+                    sb.replace(start, end, value.toString());
                     return sb.toString();
                 }
 
@@ -40,15 +41,14 @@
     }
 
     @Override
-    public String extract(String object) {
-        String string = (String) object;
-        Matcher matcher = pattern.matcher(string);
+    public CharSequence extract(CharSequence object) {
+        Matcher matcher = pattern.matcher(object);
         if (matcher.find()) {
             if (matcher.groupCount() >= 1) {
                 int start = matcher.start(1);
                 int end = matcher.end(1);
                 if (start != -1 && end != -1) {
-                    return string.substring(start, end);
+                    return object.subSequence(start, end);
                 }
             }
         }
--- a/stress-tester/src/main/java/com/passus/st/client/http/extractor/XmlValueExtractor.java	Tue Dec 05 09:36:17 2017 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/extractor/XmlValueExtractor.java	Tue Dec 05 11:07:07 2017 +0100
@@ -3,6 +3,7 @@
 import com.passus.commons.Assert;
 import java.io.StringReader;
 import java.io.StringWriter;
+import java.util.Objects;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.transform.OutputKeys;
@@ -25,7 +26,7 @@
  *
  * @author Mirosław Hawrot
  */
-public class XmlValueExtractor implements ContentExtractor {
+public class XmlValueExtractor implements ContentExtractor, ContentReplacer {
 
     public static final Logger LOGGER = LogManager.getLogger(XmlValueExtractor.class);
 
@@ -86,15 +87,16 @@
         return null;
     }
 
-    public String replace(String content, String value) {
+    @Override
+    public CharSequence replace(CharSequence content, CharSequence value) {
         DocumentBuilder builder = getDocBuilder();
         if (builder != null) {
-            try (StringReader sr = new StringReader(content)) {
+            try (StringReader sr = new StringReader(content.toString())) {
                 Document doc = builder.parse(new InputSource(sr));
                 Node node = (Node) xPathExpr.evaluate(doc, XPathConstants.NODE);
                 Transformer transformer = getXmlTransformer();
                 if (node != null && transformer != null) {
-                    node.setTextContent(value);
+                    node.setTextContent(Objects.toString(value));
                     try (StringWriter writer = new StringWriter()) {
                         transformer.transform(new DOMSource(doc), new StreamResult(writer));
                         return writer.getBuffer().toString();
@@ -111,10 +113,14 @@
     }
 
     @Override
-    public String extract(String content) {
+    public CharSequence extract(CharSequence content) {
+        if( content == null ) {
+            return null;
+        }
+        
         DocumentBuilder builder = getDocBuilder();
         if (builder != null) {
-            try (StringReader sr = new StringReader(content)) {
+            try (StringReader sr = new StringReader(content.toString())) {
                 Document doc = builder.parse(new InputSource(sr));
                 if (returnNullIfPathNotFound) {
                     Node node = (Node) xPathExpr.evaluate(doc, XPathConstants.NODE);
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageModificationFilter.java	Tue Dec 05 09:36:17 2017 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageModificationFilter.java	Tue Dec 05 11:07:07 2017 +0100
@@ -10,16 +10,21 @@
 import static com.passus.config.schema.ConfigurationSchemaBuilder.tupleDef;
 import static com.passus.config.schema.ConfigurationSchemaBuilder.valueDef;
 import com.passus.config.schema.KeyNameVaryListNodeDefinition;
+import com.passus.config.schema.MapNodeDefinition;
 import com.passus.config.schema.NodeDefinition;
 import com.passus.config.schema.NodeDefinitionCreator;
 import com.passus.data.ByteString;
 import com.passus.filter.UnmutableValueExtractor;
 import com.passus.filter.ValueExtractor;
+import com.passus.net.http.HttpMessage;
 import com.passus.net.http.HttpMessageHelper;
 import com.passus.net.http.HttpParameters;
 import com.passus.net.http.HttpRequest;
 import com.passus.net.http.HttpResponse;
 import com.passus.st.client.http.HttpFlowContext;
+import com.passus.st.client.http.extractor.ContentExtractor;
+import com.passus.st.client.http.extractor.ContentExtractorUtils;
+import com.passus.st.client.http.extractor.ContentReplacer;
 import com.passus.st.config.HeaderOperationNodeDefinition;
 import com.passus.st.plugin.PluginConstants;
 import com.passus.st.validation.HeaderNameValidator;
@@ -463,6 +468,41 @@
 
     }
 
+    public static class ContentOperation extends Operation {
+
+        protected final HttpMessageHelper messageHelper = HttpMessageHelper.get();
+
+        private final ContentReplacer replacer;
+
+        private final CharSequence newValue;
+
+        public ContentOperation(String rule, CharSequence newValue) {
+            replacer = ContentExtractorUtils.createReplacer(rule);
+            this.newValue = newValue;
+        }
+
+        public ContentOperation(ContentReplacer replacer, CharSequence newValue) {
+            this.replacer = replacer;
+            this.newValue = newValue;
+        }
+
+        @Override
+        public void process(HttpRequest req, HttpResponse resp, HttpFlowContext context) {
+            if (req == null) {
+                return;
+            }
+
+            try {
+                replacer.replace(req, newValue);
+            } catch (Exception e) {
+                if (LOGGER.isDebugEnabled()) {
+                    LOGGER.debug(e.getMessage(), e);
+                }
+            }
+        }
+
+    }
+
     private final List<Operation> operations = new ArrayList<>();
 
     private HttpMessagePredicate predicate;
@@ -545,6 +585,10 @@
                     )
             );
 
+            HeaderOperationNodeDefinition contentNodeDef = new HeaderOperationNodeDefinition(
+                    valueDef()
+            );
+
             KeyNameVaryListNodeDefinition operationsDef = new KeyNameVaryListNodeDefinition()
                     .setNodeTransformer(new HttpMessageModificationFilterTransformer())
                     .add("$removeHeader", valueDef().addValidator(HeaderNameValidator.INSTANCE))
@@ -558,7 +602,8 @@
                     .add("$setPostParam", paramNodeDef)
                     .add("$removeQueryParam", paramNodeDef)
                     .add("$addQueryParam", paramNodeDef)
-                    .add("$setQueryParam", paramNodeDef);
+                    .add("$setQueryParam", paramNodeDef)
+                    .add("$setContent", contentNodeDef);
 
             return mapDef(
                     tupleDef("operations", operationsDef),
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTransformer.java	Tue Dec 05 09:36:17 2017 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTransformer.java	Tue Dec 05 11:07:07 2017 +0100
@@ -6,11 +6,14 @@
 import com.passus.config.CTupleNode;
 import com.passus.config.CValueNode;
 import com.passus.config.ConfigurationContext;
+import com.passus.config.ConfigurationUtils;
 import com.passus.config.NodeType;
 import com.passus.config.schema.NodeTransformer;
 import com.passus.config.validation.Errors;
 import com.passus.filter.ValueExtractor;
 import com.passus.filter.config.ExpressionNodeTransformer;
+import com.passus.st.client.http.extractor.ContentExtractorUtils;
+import com.passus.st.client.http.extractor.ContentReplacer;
 import com.passus.st.client.http.filter.HttpMessageModificationFilter.*;
 import com.passus.st.filter.Transformers;
 import static com.passus.st.validation.NodeValidationUtils.validateType;
@@ -164,6 +167,28 @@
                     case "$setqueryparam":
                         op = createNameValueOperation(tuple, errors, SetQueryParameterOperation.class);
                         break;
+                    case "$setcontent":
+                        if (validateType(tuple.getNode(), NodeType.MAP, errors)) {
+                            CTupleNode valTupleNode = ((CMapNode) tuple.getNode()).getFirstChild();
+                            if (valTupleNode != null) {
+                                String rule = valTupleNode.getName();
+                                ContentReplacer replacer;
+                                try {
+                                    replacer = ContentExtractorUtils.createReplacer(rule);
+                                } catch (Exception e) {
+                                    errors.reject(valTupleNode, "Invalid rule.");
+                                    break;
+                                }
+
+                                String newValue = ConfigurationUtils.extractString(valTupleNode.getNode(), errors);
+                                if(errors.hasError()) {
+                                    break;
+                                }
+                                
+                                op = new ContentOperation(rule, newValue);
+                            }
+                        }
+                        break;
                     default:
                         throw new IllegalStateException("Not supported operation '" + opName + "'.");
                 }
--- a/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpResponseContentExtractorFilter.java	Tue Dec 05 09:36:17 2017 +0100
+++ b/stress-tester/src/main/java/com/passus/st/client/http/filter/HttpResponseContentExtractorFilter.java	Tue Dec 05 11:07:07 2017 +0100
@@ -75,7 +75,7 @@
                 }
 
                 try {
-                    String value = contentExtractor.extract(response);
+                    CharSequence value = contentExtractor.extract(response);
                     if (value != null) {
                         params.set(paramName, value);
                         LOGGER.debug("Session variable created {}='{}'", paramName, value);
@@ -101,7 +101,7 @@
             }
 
             try {
-                String value = contentExtractor.extract(response);
+                CharSequence value = contentExtractor.extract(response);
                 if (value != null) {
                     params.set(paramName, value);
                     LOGGER.debug("Global variable created {}='{}'", paramName, value);
--- a/stress-tester/src/main/java/com/passus/st/filter/HttpMessageHeaderValueExtractor.java	Tue Dec 05 09:36:17 2017 +0100
+++ b/stress-tester/src/main/java/com/passus/st/filter/HttpMessageHeaderValueExtractor.java	Tue Dec 05 11:07:07 2017 +0100
@@ -7,6 +7,7 @@
 import com.passus.net.http.HttpMessageHelper;
 import com.passus.st.ParametersBag;
 import com.passus.st.client.http.extractor.ContentExtractor;
+import com.passus.st.client.http.extractor.ContentExtractorUtils;
 import com.passus.st.client.http.extractor.JsonValueExtractor;
 import com.passus.st.client.http.extractor.PostValueExtractor;
 import com.passus.st.client.http.extractor.RegexValueExtractor;
@@ -137,46 +138,7 @@
         }
 
         public ExtractContentOperation(String rule) {
-            Assert.notNull(rule, "rule");
-            if (rule.length() < 4) {
-                throw new IllegalArgumentException("Invalid extractor rule '" + rule + "'.");
-            }
-
-            int pos = rule.indexOf(':');
-            if (pos == -1) {
-                throw new IllegalArgumentException("Invalid extractor rule '" + rule + "'.");
-            }
-
-            try {
-                String extractorType = rule.substring(0, pos);
-                String expr = rule.substring(pos + 1);
-                switch (extractorType.toLowerCase()) {
-                    case "json":
-                        contentExtractor = new JsonValueExtractor(expr);
-                        break;
-                    case "xml":
-                        contentExtractor = new XmlValueExtractor(expr);
-                        break;
-                    case "post":
-                        contentExtractor = new PostValueExtractor(expr);
-                        break;
-                    case "regexp":
-                        contentExtractor = new RegexValueExtractor(expr);
-                        break;
-                    default:
-                        throw new IllegalArgumentException("Invalid extractor rule '" + rule + "'. Invalid extractor type '" + extractorType + "'.");
-                }
-            } catch (Exception e) {
-                if (LOGGER.isDebugEnabled()) {
-                    LOGGER.debug(e.getMessage(), e);
-                }
-
-                throw new IllegalArgumentException("Invalid extractor rule '" + rule + "'." + e.getMessage(), e);
-            }
-
-            if (contentExtractor == null) {
-                throw new IllegalArgumentException("Invalid extractor rule '" + rule + "'.");
-            }
+            contentExtractor = ContentExtractorUtils.createExtractor(rule);
         }
 
         public ExtractContentOperation(ContentExtractor contentExtractor) {
--- a/stress-tester/src/test/java/com/passus/st/client/http/extractor/ContentExtractorTransformerTest.java	Tue Dec 05 09:36:17 2017 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/extractor/ContentExtractorTransformerTest.java	Tue Dec 05 11:07:07 2017 +0100
@@ -25,7 +25,7 @@
         
         ContentExtractor extractor = transformer.transform(node.getNode());
         assertTrue(extractor instanceof RegexValueExtractor);
-        String value = extractor.extract("key1: \" value1 \", \"\"");
+        CharSequence value = extractor.extract("key1: \" value1 \", \"\"");
         assertEquals(" value1 ", value);
     }
 
@@ -35,7 +35,7 @@
         
         ContentExtractor extractor = transformer.transform(node.getNode());
         assertTrue(extractor instanceof JsonValueExtractor);
-        String value = extractor.extract("{\"node3\": {\"node4\": \" value1 \"}}");
+        CharSequence value = extractor.extract("{\"node3\": {\"node4\": \" value1 \"}}");
         assertEquals(" value1 ", value);
     }
 
@@ -45,7 +45,7 @@
         
         ContentExtractor extractor = transformer.transform(node.getNode());
         assertTrue(extractor instanceof XmlValueExtractor);
-        String value = extractor.extract("<root><node1> value1 </node1></root>");
+        CharSequence value = extractor.extract("<root><node1> value1 </node1></root>");
         assertEquals(" value1 ", value);
     }
 
--- a/stress-tester/src/test/java/com/passus/st/client/http/extractor/JsonValueExtractorTest.java	Tue Dec 05 09:36:17 2017 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/extractor/JsonValueExtractorTest.java	Tue Dec 05 11:07:07 2017 +0100
@@ -10,10 +10,14 @@
 public class JsonValueExtractorTest {
 
     private static String extract(String path, String content) {
-        return new JsonValueExtractor(path).extract(content);
+        CharSequence value = new JsonValueExtractor(path).extract(content);
+        if (value == null) {
+            return null;
+        }
+        return value.toString();
     }
 
-    private static String replace(String path, String content, String value) {
+    private static CharSequence replace(String path, String content, String value) {
         return new JsonValueExtractor(path).replace(content, value);
     }
 
--- a/stress-tester/src/test/java/com/passus/st/client/http/extractor/PostValueExtractorTest.java	Tue Dec 05 09:36:17 2017 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/extractor/PostValueExtractorTest.java	Tue Dec 05 11:07:07 2017 +0100
@@ -9,11 +9,11 @@
  * @author Mirosław Hawrot
  */
 public class PostValueExtractorTest {
-    
-    private static String replace(String fieldName, String content, String value) throws IOException {
+
+    private static CharSequence replace(String fieldName, String content, String value) throws IOException {
         return new PostValueExtractor(fieldName).replace(content, value);
     }
-    
+
     @Test
     public void testReplace() throws Exception {
         String content = "a=value1&b=value2";
--- a/stress-tester/src/test/java/com/passus/st/client/http/extractor/RegexValueExtractorTest.java	Tue Dec 05 09:36:17 2017 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/extractor/RegexValueExtractorTest.java	Tue Dec 05 11:07:07 2017 +0100
@@ -9,11 +9,11 @@
  */
 public class RegexValueExtractorTest {
 
-    private static String extract(String regex, String content) {
+    private static CharSequence extract(String regex, String content) {
         return new RegexValueExtractor(regex).extract(content);
     }
 
-    private static String replace(String regex, String content, String value) {
+    private static CharSequence replace(String regex, String content, String value) {
         return new RegexValueExtractor(regex).replace(content, value);
     }
 
--- a/stress-tester/src/test/java/com/passus/st/client/http/extractor/XmlValueExtractorTest.java	Tue Dec 05 09:36:17 2017 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/extractor/XmlValueExtractorTest.java	Tue Dec 05 11:07:07 2017 +0100
@@ -18,18 +18,23 @@
             + " <emptyNode></emptyNode>"
             + "</root>";
 
-    private static String extract(String path, String content) throws Exception {
+    private static CharSequence extract(String path, String content) throws Exception {
         return new XmlValueExtractor(path).extract(content);
     }
 
-    private static String extractWithNull(String path, String content) throws Exception {
+    private static CharSequence extractWithNull(String path, String content) throws Exception {
         XmlValueExtractor e = new XmlValueExtractor(path);
         e.setReturnNullIfPathNotFound(true);
         return e.extract(content);
     }
 
     private static String replace(String path, String content, String value) throws Exception {
-        return new XmlValueExtractor(path).replace(content, value);
+        CharSequence newValue = new XmlValueExtractor(path).replace(content, value);
+        if (newValue == null) {
+            return null;
+        }
+
+        return newValue.toString();
     }
 
     @Test
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTest.java	Tue Dec 05 09:36:17 2017 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTest.java	Tue Dec 05 11:07:07 2017 +0100
@@ -1,6 +1,7 @@
 package com.passus.st.client.http.filter;
 
 import com.passus.config.validation.Errors;
+import com.passus.net.MimeTypes;
 import com.passus.net.http.HttpHeaders;
 import com.passus.net.http.HttpMessageHelper;
 import com.passus.net.http.HttpParameters;
@@ -12,6 +13,7 @@
 import com.passus.st.client.http.filter.HttpMessageModificationFilter.AddCookieOperation;
 import com.passus.st.client.http.filter.HttpMessageModificationFilter.AddHeaderOperation;
 import com.passus.st.client.http.filter.HttpMessageModificationFilter.AddQueryParameterOperation;
+import com.passus.st.client.http.filter.HttpMessageModificationFilter.ContentOperation;
 import com.passus.st.client.http.filter.HttpMessageModificationFilter.PostDataAddParamOperation;
 import com.passus.st.client.http.filter.HttpMessageModificationFilter.PostDataRemoveParamOperation;
 import com.passus.st.client.http.filter.HttpMessageModificationFilter.PostDataSetParamOperation;
@@ -26,6 +28,7 @@
 import static org.testng.AssertJUnit.*;
 import org.testng.annotations.AfterClass;
 import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
 /**
@@ -34,6 +37,8 @@
  */
 public class HttpMessageModificationFilterTest {
 
+    private final HttpMessageHelper helper = HttpMessageHelper.get();
+    
     @BeforeClass
     public static void beforeClass() {
         AppUtils.registerAll();
@@ -44,6 +49,15 @@
         AppUtils.unregisterAll();
     }
 
+    @DataProvider(name = "contents")
+    public Object[][] contents() {
+        return new Object[][]{
+            {"param1=value1&param2=value2", "post:param2", "value2new", "param1=value1&param2=value2new", MimeTypes.APPLICATION_FORM_URLENCODED},
+            {"abc 123 def", "regexp:(\\d+)", "digits", "abc digits def", MimeTypes.TEXT_PLAIN},
+            {"{node:\"value\"}", "json:$.node", "newValue", "{\"node\":\"newValue\"}", MimeTypes.APPLICATION_JSON}
+        };
+    }
+
     @Test(enabled = true)
     public void testFilterOutbound() throws Exception {
         HttpRequest req = HttpRequestBuilder
@@ -88,6 +102,20 @@
         assertEquals("param1=value1&param5=value5&param6=value6a&param6=value6b&param2=value2a", params.toString());
     }
 
+    @Test(enabled = true, dataProvider = "contents")
+    public void testFilterOutbound_SetContent(String content, String rule, String newValue, String expectedContent, CharSequence contentType) throws Exception {
+        HttpRequest req = HttpRequestBuilder
+                .post("http://test.com/test")
+                .content(content, contentType)
+                .build();
+        HttpMessageModificationFilter filter = new HttpMessageModificationFilter();
+        filter.addOperation(new ContentOperation(rule, newValue));
+        filter.filterOutbound(req, null, null);
+        
+        String filteredContent = helper.contentToString(req);
+        assertEquals(expectedContent, filteredContent);
+    }
+
     @Test(enabled = true)
     public void testConfigureAndFilterOutbound() throws Exception {
         String filterConfig = "filters:\n"
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTransformerTest.java	Tue Dec 05 09:36:17 2017 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpMessageModificationFilterTransformerTest.java	Tue Dec 05 11:07:07 2017 +0100
@@ -45,7 +45,9 @@
                 + "  $setPostParam:\n"
                 + "    P2: value\n"
                 + "  $addPostParam:\n"
-                + "    P3: value";
+                + "    P3: value\n"
+                + "  $setContent:\n"
+                + "    regexp:aaa(.+)bbb: aaaa\n";
         CTupleNode node = read(config);
 
         Errors errors = new Errors();
@@ -53,7 +55,7 @@
         List<Operation> operations = (List) value.getValue();
 
         assertEquals(0, errors.getErrorCount());
-        assertEquals(12, operations.size());
+        assertEquals(13, operations.size());
         assertTrue(operations.get(0) instanceof RemoveHeaderOperation);
         assertTrue(operations.get(1) instanceof SetHeaderOperation);
         assertTrue(operations.get(2) instanceof AddHeaderOperation);
@@ -66,6 +68,7 @@
         assertTrue(operations.get(9) instanceof PostDataRemoveParamOperation);
         assertTrue(operations.get(10) instanceof PostDataSetParamOperation);
         assertTrue(operations.get(11) instanceof PostDataAddParamOperation);
+        assertTrue(operations.get(12) instanceof ContentOperation);
     }
 
 }
--- a/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpResponseContentExtractorFilterTest.java	Tue Dec 05 09:36:17 2017 +0100
+++ b/stress-tester/src/test/java/com/passus/st/client/http/filter/HttpResponseContentExtractorFilterTest.java	Tue Dec 05 11:07:07 2017 +0100
@@ -121,14 +121,14 @@
 
     private static class ConstContentExtractor implements ContentExtractor {
 
-        private final String value;
+        private final CharSequence value;
 
-        public ConstContentExtractor(String value) {
+        public ConstContentExtractor(CharSequence value) {
             this.value = value;
         }
 
         @Override
-        public String extract(String content) {
+        public CharSequence extract(CharSequence content) {
             return value;
         }
     }