New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HTTP Proxy XML-RPC support #1871
Comments
Jesper Thorhauge (migrated from Bugzilla): HTTPSamplerBase.javaIndex: src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java
===================================================================
--- src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java (revision 511863)
+++ src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java (working copy)
@@ -16,6 +16,7 @@
*/
package org.apache.jmeter.protocol.http.sampler;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
@@ -52,6 +53,8 @@
import org.apache.jorphan.logging.LoggingManager;
import org.apache.jorphan.util.JOrphanUtils;
import org.apache.log.Logger;
+import org.apache.xerces.parsers.DOMParser;
+import org.xml.sax.InputSource;
/**
* Common constants and methods for HTTP samplers
@@ -229,6 +232,10 @@
}
}
+ //variables used for xml-rpc call handling
+ public static DOMParser xmlRpcParser = null;
+ public static XmlRpcErrorHandler xmlRpcErrorHandler = null;
+
////////////////////// Variables //////////////////////
private boolean dynamicPath = false;// Set false if spaces are already encoded
@@ -396,6 +403,12 @@
this.getArguments().addArgument(arg);
}
+ public void addUnencodedArgument(String name, String value, String metadata) {
+ HTTPArgument arg = new HTTPArgument(name, value, metadata, false);
+ arg.setAlwaysEncoded(false);
+ this.getArguments().addArgument(arg);
+ }
+
public void addArgument(String name, String value) {
this.getArguments().addArgument(new HTTPArgument(name, value));
}
@@ -618,40 +631,44 @@
*
*/
public void parseArguments(String queryString) {
- String[] args = JOrphanUtils.split(queryString, QRY_SEP);
- for (int i = 0; i < args.length; i++) {
- // need to handle four cases:
- // - string contains name=value
- // - string contains name=
- // - string contains name
- // - empty string
-
- String metaData; // records the existance of an equal sign
- String name;
- String value;
- int length = args[i].length();
- int endOfNameIndex = args[i].indexOf(ARG_VAL_SEP);
- if (endOfNameIndex != -1) {// is there a separator?
- // case of name=value, name=
- metaData = ARG_VAL_SEP;
- name = args[i].substring(0, endOfNameIndex);
- value = args[i].substring(endOfNameIndex + 1, length);
- } else {
- metaData = "";
- name=args[i];
- value="";
- }
- if (name.length() > 0) {
- // The browser has already done the encoding, so save the values as is
- HTTPArgument arg = new HTTPArgument(name, value, metaData, false);
- // and make sure they stay that way:
- arg.setAlwaysEncoded(false);
- // Note that URL.encode()/decode() do not follow RFC3986 entirely
- this.getArguments().addArgument(arg);
- // TODO: this leaves the arguments in encoded form, which may be difficult to read
- // if we can find proper coding methods, this could be tidied up
- }
- }
+ if (isXmlRpc(queryString)) {
+ addUnencodedArgument("", queryString, "");
+ } else {
+ String[] args = JOrphanUtils.split(queryString, QRY_SEP);
+ for (int i = 0; i < args.length; i++) {
+ // need to handle four cases:
+ // - string contains name=value
+ // - string contains name=
+ // - string contains name
+ // - empty string
+
+ String metaData; // records the existance of an equal sign
+ String name;
+ String value;
+ int length = args[i].length();
+ int endOfNameIndex = args[i].indexOf(ARG_VAL_SEP);
+ if (endOfNameIndex != -1) {// is there a separator?
+ // case of name=value, name=
+ metaData = ARG_VAL_SEP;
+ name = args[i].substring(0, endOfNameIndex);
+ value = args[i].substring(endOfNameIndex + 1, length);
+ } else {
+ metaData = "";
+ name=args[i];
+ value="";
+ }
+ if (name.length() > 0) {
+ // The browser has already done the encoding, so save the values as is
+ HTTPArgument arg = new HTTPArgument(name, value, metaData, false);
+ // and make sure they stay that way:
+ arg.setAlwaysEncoded(false);
+ // Note that URL.encode()/decode() do not follow RFC3986 entirely
+ this.getArguments().addArgument(arg);
+ // TODO: this leaves the arguments in encoded form, which may be difficult to read
+ // if we can find proper coding methods, this could be tidied up
+ }
+ }
+ }
}
public String toString() {
@@ -1009,5 +1026,33 @@
public static boolean isSecure(URL url){
return isSecure(url.getProtocol());
}
+
+ /**
+ * check for xmlrpc request using xsd from http://www.ibiblio.org/xml/books/xmljava/chapters/ch02s05.html (no official xsd exists)
+ *
+ * @param queryString
+ * @return
+ */
+ private boolean isXmlRpc(String queryString) {
+ try {
+ if (!queryString.startsWith("<?xml"))
+ return false;
+
+ if (xmlRpcParser == null) {
+ xmlRpcParser = new DOMParser();
+ xmlRpcParser.setFeature("http://xml.org/sax/features/validation", true);
+ xmlRpcParser.setFeature("http://apache.org/xml/features/validation/schema", true);
+ xmlRpcParser.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation", "xml-rpc.xsd");
+ xmlRpcErrorHandler = new XmlRpcErrorHandler();
+ xmlRpcParser.setErrorHandler(xmlRpcErrorHandler);
+ }
+ xmlRpcParser.parse(new InputSource(new ByteArrayInputStream(queryString.getBytes())));
+ return xmlRpcErrorHandler.getParseErrors().length == 0;
+ } catch (Exception e) {
+ log.debug("isXmlRpc() " + e);
+ }
+ return false;
+ }
+
}
|
Jesper Thorhauge (migrated from Bugzilla): XmlRpcErrorHandler.javaIndex: src/protocol/http/org/apache/jmeter/protocol/http/sampler/XmlRpcErrorHandler.java
===================================================================
--- src/protocol/http/org/apache/jmeter/protocol/http/sampler/XmlRpcErrorHandler.java (revision 0)
+++ src/protocol/http/org/apache/jmeter/protocol/http/sampler/XmlRpcErrorHandler.java (revision 0)
@@ -0,0 +1,24 @@
+package org.apache.jmeter.protocol.http.sampler;
+
+import java.util.Stack;
+
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class XmlRpcErrorHandler extends DefaultHandler {
+
+ private Stack parseErrors = null;
+
+ public SAXParseException[] getParseErrors() {
+ if (parseErrors == null)
+ parseErrors = new Stack();
+ return (SAXParseException[])parseErrors.toArray(new SAXParseException[0]);
+ }
+
+ public void error(SAXParseException e) {
+ if (parseErrors == null)
+ parseErrors = new Stack();
+ parseErrors.push(e);
+ }
+
+} |
Jesper Thorhauge (migrated from Bugzilla): xml-rpc.xsdIndex: bin/xml-rpc.xsd
===================================================================
--- bin/xml-rpc.xsd (revision 0)
+++ bin/xml-rpc.xsd (revision 0)
@@ -0,0 +1,132 @@
+<?xml version="1.0"?>
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <!-- The only two possible root elements are methodResponse and
+ methodCall so these are the only two I use a top-level
+ declaration for. -->
+
+ <xsd:element name="methodCall">
+ <xsd:complexType>
+ <xsd:all>
+ <xsd:element name="methodName">
+ <xsd:simpleType>
+ <xsd:restriction base="ASCIIString">
+ <xsd:pattern value="([A-Za-z0-9]|/|\.|:|_)*" />
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:element>
+ <xsd:element name="params" minOccurs="0" maxOccurs="1">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="param" type="ParamType"
+ minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:all>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="methodResponse">
+ <xsd:complexType>
+ <xsd:choice>
+ <xsd:element name="params">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="param" type="ParamType"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="fault">
+ <!-- What can appear inside a fault is very restricted -->
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="struct">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="member"
+ type="MemberType">
+ </xsd:element>
+ <xsd:element name="member"
+ type="MemberType">
+ </xsd:element>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:complexType name="ParamType">
+ <xsd:sequence>
+ <xsd:element name="value" type="ValueType"/>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <xsd:complexType name="ValueType" mixed="true">
+ <!-- I need to figure out how to say that this
+ is either a simple xsd:string type or that
+ it contains one of these elements; but that otherwise
+ it does not have mixed content -->
+ <xsd:choice>
+ <xsd:element name="i4" type="xsd:int"/>
+ <xsd:element name="int" type="xsd:int"/>
+ <xsd:element name="string" type="ASCIIString"/>
+ <xsd:element name="double" type="xsd:decimal"/>
+ <xsd:element name="Base64" type="xsd:base64Binary"/>
+ <xsd:element name="boolean" type="NumericBoolean"/>
+ <xsd:element name="dateTime.iso8601" type="xsd:dateTime"/>
+ <xsd:element name="array" type="ArrayType"/>
+ <xsd:element name="struct" type="StructType"/>
+ </xsd:choice>
+ </xsd:complexType>
+
+ <xsd:complexType name="StructType">
+ <xsd:sequence>
+ <xsd:element name="member" type="MemberType"
+ maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <xsd:complexType name="MemberType">
+ <xsd:sequence>
+ <xsd:element name="name" type="xsd:string" />
+ <xsd:element name="value" type="ValueType"/>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <xsd:complexType name="ArrayType">
+ <xsd:sequence>
+ <xsd:element name="data">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:element name="value" type="ValueType"
+ minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:sequence>
+ </xsd:complexType>
+
+ <xsd:simpleType name="ASCIIString">
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="([ -~]|\n|\r|\t)*" />
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="NumericBoolean">
+ <xsd:restriction base="xsd:boolean">
+ <xsd:pattern value="0|1" />
+ </xsd:restriction>
+ </xsd:simpleType>
+
+</xsd:schema>
\ No newline at end of file |
Jesper Thorhauge (migrated from Bugzilla): HTTPSamplerBase.javaIndex: src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java
===================================================================
--- src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java (revision 512300)
+++ src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java (working copy)
@@ -16,6 +16,7 @@
*/
package org.apache.jmeter.protocol.http.sampler;
+import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
@@ -52,6 +53,11 @@
import org.apache.jorphan.logging.LoggingManager;
import org.apache.jorphan.util.JOrphanUtils;
import org.apache.log.Logger;
+import org.apache.xerces.parsers.DOMParser;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
/**
* Common constants and methods for HTTP samplers
@@ -396,6 +402,12 @@
this.getArguments().addArgument(arg);
}
+ public void addUnencodedArgument(String name, String value, String metadata) {
+ HTTPArgument arg = new HTTPArgument(name, value, metadata, false);
+ arg.setAlwaysEncoded(false);
+ this.getArguments().addArgument(arg);
+ }
+
public void addArgument(String name, String value) {
this.getArguments().addArgument(new HTTPArgument(name, value));
}
@@ -618,40 +630,44 @@
*
*/
public void parseArguments(String queryString) {
- String[] args = JOrphanUtils.split(queryString, QRY_SEP);
- for (int i = 0; i < args.length; i++) {
- // need to handle four cases:
- // - string contains name=value
- // - string contains name=
- // - string contains name
- // - empty string
-
- String metaData; // records the existance of an equal sign
- String name;
- String value;
- int length = args[i].length();
- int endOfNameIndex = args[i].indexOf(ARG_VAL_SEP);
- if (endOfNameIndex != -1) {// is there a separator?
- // case of name=value, name=
- metaData = ARG_VAL_SEP;
- name = args[i].substring(0, endOfNameIndex);
- value = args[i].substring(endOfNameIndex + 1, length);
- } else {
- metaData = "";
- name=args[i];
- value="";
- }
- if (name.length() > 0) {
- // The browser has already done the encoding, so save the values as is
- HTTPArgument arg = new HTTPArgument(name, value, metaData, false);
- // and make sure they stay that way:
- arg.setAlwaysEncoded(false);
- // Note that URL.encode()/decode() do not follow RFC3986 entirely
- this.getArguments().addArgument(arg);
- // TODO: this leaves the arguments in encoded form, which may be difficult to read
- // if we can find proper coding methods, this could be tidied up
- }
- }
+ if (isXmlRpc(queryString)) {
+ addUnencodedArgument("", queryString, "");
+ } else {
+ String[] args = JOrphanUtils.split(queryString, QRY_SEP);
+ for (int i = 0; i < args.length; i++) {
+ // need to handle four cases:
+ // - string contains name=value
+ // - string contains name=
+ // - string contains name
+ // - empty string
+
+ String metaData; // records the existance of an equal sign
+ String name;
+ String value;
+ int length = args[i].length();
+ int endOfNameIndex = args[i].indexOf(ARG_VAL_SEP);
+ if (endOfNameIndex != -1) {// is there a separator?
+ // case of name=value, name=
+ metaData = ARG_VAL_SEP;
+ name = args[i].substring(0, endOfNameIndex);
+ value = args[i].substring(endOfNameIndex + 1, length);
+ } else {
+ metaData = "";
+ name=args[i];
+ value="";
+ }
+ if (name.length() > 0) {
+ // The browser has already done the encoding, so save the values as is
+ HTTPArgument arg = new HTTPArgument(name, value, metaData, false);
+ // and make sure they stay that way:
+ arg.setAlwaysEncoded(false);
+ // Note that URL.encode()/decode() do not follow RFC3986 entirely
+ this.getArguments().addArgument(arg);
+ // TODO: this leaves the arguments in encoded form, which may be difficult to read
+ // if we can find proper coding methods, this could be tidied up
+ }
+ }
+ }
}
public String toString() {
@@ -1009,5 +1025,37 @@
public static boolean isSecure(URL url){
return isSecure(url.getProtocol());
}
+
+ /**
+ * Check for xmlrpc request using xsd from http://www.ibiblio.org/xml/books/xmljava/chapters/ch02s05.html (no official xsd exists)
+ *
+ * @param queryString
+ * @return
+ */
+ private boolean isXmlRpc(String queryString) {
+ try {
+ if (!queryString.startsWith("<?xml"))
+ return false;
+
+ DOMParser xmlRpcParser = new DOMParser();
+ xmlRpcParser.setFeature("http://xml.org/sax/features/validation", true);
+ xmlRpcParser.setFeature("http://apache.org/xml/features/validation/schema", true);
+ xmlRpcParser.setProperty("http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation", "xml-rpc.xsd");
+ XmlRpcErrorHandler xmlRpcErrorHandler = new XmlRpcErrorHandler();
+ xmlRpcParser.setErrorHandler(xmlRpcErrorHandler);
+ xmlRpcParser.parse(new InputSource(new ByteArrayInputStream(queryString.getBytes())));
+ return xmlRpcErrorHandler.getParseErrors().length == 0;
+ } catch (SAXNotSupportedException snse) {
+ log.warn("isXmlRpc() " + snse);
+ } catch (SAXNotRecognizedException snre) {
+ log.warn("isXmlRpc() " + snre);
+ } catch (SAXException se) {
+ log.warn("isXmlRpc() " + se);
+ } catch (IOException ioe) {
+ log.warn("isXmlRpc() " + ioe);
+ }
+ return false;
+ }
+
}
|
Jesper Thorhauge (migrated from Bugzilla): XmlRpcErrorHandler.java/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jmeter.protocol.http.sampler;
import java.util.Stack;
import org.xml.sax.SAXParseException;
import org.xml.sax.helpers.DefaultHandler;
public class XmlRpcErrorHandler extends DefaultHandler {
private Stack parseErrors = null;
public SAXParseException[] getParseErrors() {
if (parseErrors == null)
parseErrors = new Stack();
return (SAXParseException[])parseErrors.toArray(new SAXParseException[0]);
}
public void error(SAXParseException e) {
if (parseErrors == null)
parseErrors = new Stack();
parseErrors.push(e);
}
} |
Jesper Thorhauge (migrated from Bugzilla): xml-rpc.xsd
|
Jesper Thorhauge (migrated from Bugzilla): Created attachment HTTPSamplerBase.java: HTTPSamplerBase supporting xml-rpc and other not-"name=value" postdata. HTTPSamplerBase.javaIndex: src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java
===================================================================
--- src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java (revision 513802)
+++ src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPSamplerBase.java (working copy)
@@ -395,7 +395,13 @@
}
this.getArguments().addArgument(arg);
}
-
+
+ public void addNonEncodedArgument(String name, String value, String metadata) {
+ HTTPArgument arg = new HTTPArgument(name, value, metadata, false);
+ arg.setAlwaysEncoded(false);
+ this.getArguments().addArgument(arg);
+ }
+
public void addArgument(String name, String value) {
this.getArguments().addArgument(new HTTPArgument(name, value));
} |
Jesper Thorhauge (migrated from Bugzilla): HttpRequestHdr.javaIndex: src/protocol/http/org/apache/jmeter/protocol/http/proxy/HttpRequestHdr.java
===================================================================
--- src/protocol/http/org/apache/jmeter/protocol/http/proxy/HttpRequestHdr.java (revision 513802)
+++ src/protocol/http/org/apache/jmeter/protocol/http/proxy/HttpRequestHdr.java (working copy)
@@ -246,8 +246,10 @@
sampler.setFileField(urlConfig.getFileFieldName());
sampler.setFilename(urlConfig.getFilename());
sampler.setMimetype(urlConfig.getMimeType());
+ } else if (postData != null && postData.trim().startsWith("<?")) {
+ sampler.addNonEncodedArgument("", postData, ""); //used when postData is pure xml (ex. an xml-rpc call)
} else {
- sampler.parseArguments(postData);
+ sampler.parseArguments(postData); //standard name=value postData
}
if (log.isDebugEnabled())
log.debug("sampler path = " + sampler.getPath()); |
Sebb (migrated from Bugzilla): Now fixed in SVN. It will be in the next release and the next nightly build at: |
Jesper Thorhauge (Bug 41707):
JMeters http proxy does not record xml-rpc calls correctly. An xml-rpc call is
not posted in the usual name=value structure, but simply as one big chunk of
xml. I have made a patch which first of all check if the postdata is xml, by
using some very simple string comparison on the query string. If the postdata
seems to be xml, it is parsed as xml and checked against an un-official xml-rpc
xsd schema taken from ibiblio.org. If any error occurs or the xml-rpc cannot be
validated against the schema, the request is recorded the usual way.
This is my first patch for the jmeter project, so please bear with me.
OS: All
The text was updated successfully, but these errors were encountered: