Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Basic functionality

  • Loading branch information...
commit a512af2c578d7c0c5ac15c512cdf28479870fe63 1 parent 45498a4
@steeltomato authored
Showing with 6,888 additions and 9 deletions.
  1. +69 −0 .classpath
  2. +5 −0 .gitignore
  3. +17 −0 .project
  4. +1,922 −0 LICENSE
  5. +22 −9 README
  6. +1 −0  build.properties
  7. +82 −0 build.xml
  8. +825 −0 src/protocol/amf/org/apache/jmeter/protocol/amf/gui/AmfProxyControlGui.java
  9. +204 −0 src/protocol/amf/org/apache/jmeter/protocol/amf/gui/AmfRequestDefaultsGui.java
  10. +188 −0 src/protocol/amf/org/apache/jmeter/protocol/amf/gui/AmfRequestGui.java
  11. +155 −0 src/protocol/amf/org/apache/jmeter/protocol/amf/proxy/AmfDaemon.java
  12. +580 −0 src/protocol/amf/org/apache/jmeter/protocol/amf/proxy/AmfProxy.java
  13. +1,002 −0 src/protocol/amf/org/apache/jmeter/protocol/amf/proxy/AmfProxyControl.java
  14. +6 −0 src/protocol/amf/org/apache/jmeter/protocol/amf/proxy/AmfProxyControlResources.properties
  15. +616 −0 src/protocol/amf/org/apache/jmeter/protocol/amf/proxy/AmfRequestHdr.java
  16. +132 −0 src/protocol/amf/org/apache/jmeter/protocol/amf/proxy/FormCharSetFinder.java
  17. +290 −0 src/protocol/amf/org/apache/jmeter/protocol/amf/proxy/HttpReplyHdr.java
  18. 0  src/protocol/amf/org/apache/jmeter/protocol/amf/resources/messages.properties
  19. +297 −0 src/protocol/amf/org/apache/jmeter/protocol/amf/sampler/AmfRequest.java
  20. +55 −0 src/protocol/amf/org/apache/jmeter/protocol/amf/sampler/AmfRequestFactory.java
  21. +11 −0 src/protocol/amf/org/apache/jmeter/protocol/amf/sampler/AmfRequestResources.properties
  22. +47 −0 src/protocol/amf/org/apache/jmeter/protocol/amf/util/ASObjectConverter.java
  23. +21 −0 src/protocol/amf/org/apache/jmeter/protocol/amf/util/AmfResources.java
  24. +175 −0 src/protocol/amf/org/apache/jmeter/protocol/amf/util/AmfXmlConverter.java
  25. +6 −0 src/protocol/amf/org/apache/jmeter/protocol/amf/util/SampleRequestVO.java
  26. +160 −0 test/src/AmfXmlConverterTest.java
View
69 .classpath
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" output="build/protocol/amf" path="src/protocol/amf"/>
+ <classpathentry kind="src" output="build/test/src" path="test/src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/jakarta-jmeter"/>
+ <classpathentry kind="lib" path="lib/commons-logging-1.1.1.jar"/>
+ <classpathentry kind="lib" path="lib/activation-1.1.1.jar"/>
+ <classpathentry kind="lib" path="lib/avalon-framework-4.1.4.jar"/>
+ <classpathentry kind="lib" path="lib/blazeds-common-4.0.0.14931.jar" sourcepath="/opt/lib-src/blazeds-src-4.0.0.14931/modules/common/src"/>
+ <classpathentry kind="lib" path="lib/blazeds-core-4.0.0.14931.jar" sourcepath="/opt/lib-src/blazeds-src-4.0.0.14931/modules/core/src"/>
+ <classpathentry kind="lib" path="lib/bsf-2.4.0.jar"/>
+ <classpathentry kind="lib" path="lib/bsf-api-3.1.jar"/>
+ <classpathentry kind="lib" path="lib/bsh-2.0b5.jar"/>
+ <classpathentry kind="lib" path="lib/bshclient.jar"/>
+ <classpathentry kind="lib" path="lib/commons-codec-1.5.jar"/>
+ <classpathentry kind="lib" path="lib/commons-collections-3.2.1.jar"/>
+ <classpathentry kind="lib" path="lib/commons-httpclient-3.1.jar"/>
+ <classpathentry kind="lib" path="lib/commons-io-2.0.1.jar"/>
+ <classpathentry kind="lib" path="lib/commons-jexl-1.1.jar"/>
+ <classpathentry kind="lib" path="lib/commons-lang-2.6.jar"/>
+ <classpathentry kind="lib" path="lib/commons-net-3.0.1.jar"/>
+ <classpathentry kind="lib" path="lib/excalibur-datasource-1.1.1.jar"/>
+ <classpathentry kind="lib" path="lib/excalibur-instrument-1.0.jar"/>
+ <classpathentry kind="lib" path="lib/excalibur-logger-1.1.jar"/>
+ <classpathentry kind="lib" path="lib/excalibur-pool-1.2.jar"/>
+ <classpathentry kind="lib" path="lib/geronimo-jms_1.1_spec-1.1.1.jar"/>
+ <classpathentry kind="lib" path="lib/htmllexer-2.0-20060923.jar"/>
+ <classpathentry kind="lib" path="lib/htmlparser-2.0-20060923.jar"/>
+ <classpathentry kind="lib" path="lib/httpclient-4.1.2.jar"/>
+ <classpathentry kind="lib" path="lib/httpcore-4.1.3.jar"/>
+ <classpathentry kind="lib" path="lib/httpmime-4.1.2.jar"/>
+ <classpathentry kind="lib" path="lib/jCharts-0.7.5.jar"/>
+ <classpathentry kind="lib" path="lib/jdom-1.1.jar"/>
+ <classpathentry kind="lib" path="lib/jorphan.jar"/>
+ <classpathentry kind="lib" path="lib/js-1.6R5.jar"/>
+ <classpathentry kind="lib" path="lib/jtidy-r938.jar"/>
+ <classpathentry kind="lib" path="lib/junit-4.8.2.jar"/>
+ <classpathentry kind="lib" path="lib/logkit-2.0.jar"/>
+ <classpathentry kind="lib" path="lib/mail-1.4.3.jar"/>
+ <classpathentry kind="lib" path="lib/oro-2.0.8.jar"/>
+ <classpathentry kind="lib" path="lib/serializer-2.7.1.jar"/>
+ <classpathentry kind="lib" path="lib/soap-2.3.1.jar"/>
+ <classpathentry kind="lib" path="lib/xalan-2.7.1.jar"/>
+ <classpathentry kind="lib" path="lib/xercesImpl-2.9.1.jar"/>
+ <classpathentry kind="lib" path="lib/xml-apis-1.3.04.jar"/>
+ <classpathentry kind="lib" path="lib/xmlgraphics-commons-1.3.1.jar"/>
+ <classpathentry kind="lib" path="lib/xpp3_min-1.1.4c.jar"/>
+ <classpathentry kind="lib" path="lib/xstream-1.3.1.jar"/>
+ <classpathentry kind="lib" path="lib/api/bcmail-jdk15-1.45.jar"/>
+ <classpathentry kind="lib" path="lib/api/bcprov-jdk15-1.45.jar"/>
+ <classpathentry kind="lib" path="lib/doc/velocity-1.6.2.jar"/>
+ <classpathentry kind="lib" path="lib/ext/ApacheJMeter_components.jar"/>
+ <classpathentry kind="lib" path="lib/ext/ApacheJMeter_core.jar"/>
+ <classpathentry kind="lib" path="lib/ext/ApacheJMeter_ftp.jar"/>
+ <classpathentry kind="lib" path="lib/ext/ApacheJMeter_functions.jar"/>
+ <classpathentry kind="lib" path="lib/ext/ApacheJMeter_http.jar"/>
+ <classpathentry kind="lib" path="lib/ext/ApacheJMeter_java.jar"/>
+ <classpathentry kind="lib" path="lib/ext/ApacheJMeter_jdbc.jar"/>
+ <classpathentry kind="lib" path="lib/ext/ApacheJMeter_jms.jar"/>
+ <classpathentry kind="lib" path="lib/ext/ApacheJMeter_junit.jar"/>
+ <classpathentry kind="lib" path="lib/ext/ApacheJMeter_ldap.jar"/>
+ <classpathentry kind="lib" path="lib/ext/ApacheJMeter_mail.jar"/>
+ <classpathentry kind="lib" path="lib/ext/ApacheJMeter_monitors.jar"/>
+ <classpathentry kind="lib" path="lib/ext/ApacheJMeter_report.jar"/>
+ <classpathentry kind="lib" path="lib/ext/ApacheJMeter_tcp.jar"/>
+ <classpathentry kind="lib" path="lib/junit/test.jar"/>
+ <classpathentry kind="output" path="build"/>
+</classpath>
View
5 .gitignore
@@ -0,0 +1,5 @@
+/lib
+/bin
+/.DS_Store
+/dist/
+/build/
View
17 .project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>jmeter-amf</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
View
1,922 LICENSE
1,922 additions, 0 deletions not shown
View
31 README
@@ -1,8 +1,6 @@
### JMeter AMF Plugin
-----------------------
-Note: This project is currently in an alpha state. Source code will be added shortly. Click the Watch button for updates.
-
Welcome to the JMeter-AMF Plugin Project. This plugin gives JMeter the ability to load test applications
using the AMF3 protocol. Main features:
@@ -10,13 +8,14 @@ using the AMF3 protocol. Main features:
* Translate AMF to XML for easy manipulation
* Set override parameters to provide each virtual user with unique Client and Session IDs
+Homepage: http://github.com/steeltomato/jmeter-amf
+
### Requirements
-----------------------
* JMeter 2.x
-* Application that utilizes BlazeDS for communication.
+* Application that utilizes BlazeDS for AMF communication.
* Want to see more libraries supported? Let me know in the issue tracker.
-* JAR(s) containing all necessary remoting classes (VOs)
### Installation
-----------------------
@@ -27,10 +26,24 @@ using the AMF3 protocol. Main features:
3. Download the version of BlazeDS that your application uses and copy blazeds-common.jar and blazeds-core.jar to jmeter/lib/ext.
-3. Create a JAR containing any objects used in AMF Remoting, commonly referred to as VOs.
- * If a class is missing, it will probably not be included in the captured XML or delivered AMF.
- * A warning will be given _somewhere_.
- * Easy way: Select class files in Eclipse, Right-click -> Export, Select location and Finish
+### Credits
+-----------------------
+
+This project was created and is maintained by Kenneth Hill (kennethjhill@gmail.com)
+
+Special thanks to the Apache Software Foundation and the contributors of the
+Jakarta JMeter project. Without your hard work, this project obviously wouldn't be possible.
+
+Copyright 2011 the original author or authors.
+
+Licensed 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
-4. Either copy remoting JAR(s) to jmeter/lib/ext or add to the classpath with the Test Plan node.
+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.
View
1  build.properties
@@ -0,0 +1 @@
+release.version=0.1
View
82 build.xml
@@ -0,0 +1,82 @@
+<project name="JMeter" default="help" basedir=".">
+ <description>
+ JMeter AMF
+ </description>
+
+ <property file="build.properties" />
+
+ <property name="src.dir" value="src/protocol/amf" />
+ <property name="lib.dir" value="lib" />
+ <property name="lib.api" value="lib/api" />
+ <property name="lib.ext" value="lib/ext" />
+ <property name="build.dir" value="build" />
+ <property name="build.amf" value="build/protocol/amf" />
+ <property name="dist.dir" value="dist" />
+
+ <property name="install.dir" value="/opt/jmeter-2.5/lib/ext" />
+
+ <property name="dest.jar" value="bin" />
+
+ <!-- Compilation Params -->
+ <property name="optimize" value="on" />
+ <property name="deprecation" value="off" />
+ <property name="target.java.version" value="1.5" />
+ <property name="src.java.version" value="1.5" />
+ <property name="encoding" value="UTF-8" />
+ <property name="includeAntRuntime" value="false" />
+
+ <path id="srcpath">
+ <pathelement location="${src.dir}" />
+ </path>
+
+ <path id="classpath">
+ <fileset dir="${lib.dir}" includes="*.jar" />
+ <fileset dir="${lib.api}" includes="*.jar" />
+ <fileset dir="${lib.ext}" includes="*.jar" />
+ </path>
+
+ <target name="init">
+ <tstamp />
+
+ <echo>JMeter-AMF v${release.version}</echo>
+ </target>
+
+ <target name="help">
+
+ </target>
+
+ <target name="clean" depends="init">
+ <delete dir="${build.dir}" />
+ <delete dir="${dist.dir}" />
+ <delete file="${dest.jar}/JMeter-AMF.jar" />
+ </target>
+
+ <target name="compile" depends="init" description="Compile JMeter-AMF classes">
+ <mkdir dir="${build.amf}"/>
+ <javac srcdir="${src.dir}" destdir="${build.amf}" optimize="${optimize}" source="${src.java.version}" debug="on" target="${target.java.version}"
+ includeAntRuntime="${includeAntRuntime}" deprecation="${deprecation}" encoding="${encoding}">
+ <include name="**/*.java" />
+ <classpath>
+ <path refid="classpath" />
+ </classpath>
+ </javac>
+ </target>
+
+ <target name="package" depends="init, compile" description="JAR the plugin">
+ <jar jarfile="${dest.jar}/JMeter-AMF.jar"
+ basedir="${build.amf}">
+ </jar>
+ </target>
+
+ <target name="install" depends="init, clean, compile, package" description="Copy JMeter-AMF to the JMeter install directory">
+ <delete file="${install.dir}/JMeter-AMF.jar" />
+ <copy file="${dest.jar}/JMeter-AMF.jar" tofile="${install.dir}/JMeter-AMF.jar" />
+ </target>
+
+ <target name="dist" depends="init, clean, compile, package" description="Build a tarball for the current version">
+ <tar destfile="${dist.dir}/JMeter-AMF-${release.version}.tar.gz"
+ compression="gzip"
+ basedir="${basedir}"
+ includes="${dest.jar}/JMeter-AMF.jar,README,LICENSE" />
+ </target>
+</project>
View
825 src/protocol/amf/org/apache/jmeter/protocol/amf/gui/AmfProxyControlGui.java
@@ -0,0 +1,825 @@
+/*
+* Copyright 2011 the original author or authors.
+*
+* Licensed 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.amf.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.io.IOException;
+import java.net.BindException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.JTextField;
+
+import org.apache.jmeter.control.Controller;
+import org.apache.jmeter.control.gui.LogicControllerGui;
+import org.apache.jmeter.engine.util.ValueReplacer;
+import org.apache.jmeter.functions.InvalidVariableException;
+import org.apache.jmeter.gui.GuiPackage;
+import org.apache.jmeter.gui.JMeterGUIComponent;
+import org.apache.jmeter.gui.UnsharedComponent;
+import org.apache.jmeter.gui.tree.JMeterTreeNode;
+import org.apache.jmeter.gui.util.HeaderAsPropertyRenderer;
+import org.apache.jmeter.gui.util.HorizontalPanel;
+import org.apache.jmeter.gui.util.MenuFactory;
+import org.apache.jmeter.gui.util.PowerTableModel;
+import org.apache.jmeter.gui.util.VerticalPanel;
+import org.apache.jmeter.protocol.amf.proxy.AmfProxyControl;
+import org.apache.jmeter.protocol.http.proxy.ProxyControl;
+import org.apache.jmeter.protocol.http.proxy.gui.ProxyControlGui;
+import org.apache.jmeter.protocol.http.sampler.HTTPSamplerFactory;
+import org.apache.jmeter.testelement.TestElement;
+import org.apache.jmeter.testelement.TestPlan;
+import org.apache.jmeter.testelement.WorkBench;
+import org.apache.jmeter.testelement.property.PropertyIterator;
+import org.apache.jmeter.util.JMeterUtils;
+import org.apache.jorphan.logging.LoggingManager;
+import org.apache.log.Logger;
+
+public class AmfProxyControlGui extends LogicControllerGui implements JMeterGUIComponent, ActionListener, ItemListener,
+ KeyListener, UnsharedComponent {
+ private static final Logger log = LoggingManager.getLoggerForClass();
+
+ private static final long serialVersionUID = 232L;
+
+ private JTextField portField;
+
+ /**
+ * Used to indicate that HTTP request headers should be captured. The
+ * default is to capture the HTTP request headers, which are specific to
+ * particular browser settings.
+ */
+ private JCheckBox httpHeaders;
+
+ /**
+ * Whether to group requests together based on inactivity separation periods --
+ * and how to handle such grouping afterwards.
+ */
+ private JComboBox groupingMode;
+
+ /**
+ * Add an Assertion to the first sample of each set
+ */
+ private JCheckBox addAssertions;
+
+ /**
+ * Set/clear the Use Keep-Alive box on the samplers (default is true)
+ */
+ private JCheckBox useKeepAlive;
+
+ /*
+ * Use regexes to match the source data
+ */
+ private JCheckBox regexMatch;
+
+ /**
+ * The list of sampler type names to choose from
+ */
+ private JComboBox samplerTypeName;
+
+ /**
+ * Set/clear the Redirect automatically box on the samplers (default is false)
+ */
+ private JCheckBox samplerRedirectAutomatically;
+
+ /**
+ * Set/clear the Follow-redirects box on the samplers (default is true)
+ */
+ private JCheckBox samplerFollowRedirects;
+
+ /**
+ * Set/clear the Download images box on the samplers (default is false)
+ */
+ private JCheckBox samplerDownloadImages;
+
+ /*
+ * Spoof the client into thinking that it is communicating with http
+ * even if it is really https.
+ */
+ private JCheckBox httpsSpoof;
+
+ /*
+ * Only spoof the URLs that match (optional)
+ */
+ private JTextField httpsMatch;
+
+ /**
+ * Regular expression to include results based on content type
+ */
+ private JTextField contentTypeInclude;
+
+ /**
+ * Regular expression to exclude results based on content type
+ */
+ private JTextField contentTypeExclude;
+
+ /**
+ * List of available target controllers
+ */
+ private JComboBox targetNodes;
+
+ private DefaultComboBoxModel targetNodesModel;
+
+ private AmfProxyControl model;
+
+ private JTable excludeTable;
+
+ private PowerTableModel excludeModel;
+
+ private JTable includeTable;
+
+ private PowerTableModel includeModel;
+
+ private static final String CHANGE_TARGET = "change_target"; // $NON-NLS-1$
+
+ private JButton stop, start, restart;
+
+ //+ action names
+ private static final String STOP = "stop"; // $NON-NLS-1$
+
+ private static final String START = "start"; // $NON-NLS-1$
+
+ private static final String RESTART = "restart"; // $NON-NLS-1$
+
+ // This is applied to fields that should cause a restart when changed
+ private static final String ENABLE_RESTART = "enable_restart"; // $NON-NLS-1$
+
+ private static final String ADD_INCLUDE = "add_include"; // $NON-NLS-1$
+
+ private static final String ADD_EXCLUDE = "add_exclude"; // $NON-NLS-1$
+
+ private static final String DELETE_INCLUDE = "delete_include"; // $NON-NLS-1$
+
+ private static final String DELETE_EXCLUDE = "delete_exclude"; // $NON-NLS-1$
+ //- action names
+
+ // Resource names for column headers
+ private static final String INCLUDE_COL = "patterns_to_include"; // $NON-NLS-1$
+
+ private static final String EXCLUDE_COL = "patterns_to_exclude"; // $NON-NLS-1$
+
+ // Used by itemListener
+ private static final String PORTFIELD = "portField"; // $NON-NLS-1$
+
+ public AmfProxyControlGui() {
+ super();
+ log.debug("Creating AmfProxyControlGui");
+ init();
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public TestElement createTestElement() {
+ model = makeProxyControl();
+ log.debug("creating/configuring model = " + model);
+ modifyTestElement(model);
+ return model;
+ }
+
+ protected AmfProxyControl makeProxyControl() {
+ AmfProxyControl local = new AmfProxyControl();
+ return local;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void modifyTestElement(TestElement el) {
+ if (excludeTable.isEditing()) {// Bug 42948
+ excludeTable.getCellEditor().stopCellEditing();
+ }
+ if (includeTable.isEditing()) {// Bug 42948
+ includeTable.getCellEditor().stopCellEditing();
+ }
+ configureTestElement(el);
+ if (el instanceof AmfProxyControl) {
+ model = (AmfProxyControl) el;
+ model.setPort(portField.getText());
+ setIncludeListInProxyControl(model);
+ setExcludeListInProxyControl(model);
+ model.setCaptureHttpHeaders(httpHeaders.isSelected());
+ model.setGroupingMode(groupingMode.getSelectedIndex());
+ model.setAssertions(addAssertions.isSelected());
+ model.setSamplerTypeName(samplerTypeName.getSelectedIndex());
+ model.setSamplerRedirectAutomatically(samplerRedirectAutomatically.isSelected());
+ model.setSamplerFollowRedirects(samplerFollowRedirects.isSelected());
+ model.setUseKeepAlive(useKeepAlive.isSelected());
+ model.setSamplerDownloadImages(samplerDownloadImages.isSelected());
+ model.setRegexMatch(regexMatch.isSelected());
+ model.setHttpsSpoof(httpsSpoof.isSelected());
+ model.setHttpsSpoofMatch(httpsMatch.getText());
+ model.setContentTypeInclude(contentTypeInclude.getText());
+ model.setContentTypeExclude(contentTypeExclude.getText());
+ TreeNodeWrapper nw = (TreeNodeWrapper) targetNodes.getSelectedItem();
+ if (nw == null) {
+ model.setTarget(null);
+ } else {
+ model.setTarget(nw.getTreeNode());
+ }
+ }
+ }
+
+ protected void setIncludeListInProxyControl(AmfProxyControl element) {
+ List<String> includeList = getDataList(includeModel, INCLUDE_COL);
+ element.setIncludeList(includeList);
+ }
+
+ protected void setExcludeListInProxyControl(AmfProxyControl element) {
+ List<String> excludeList = getDataList(excludeModel, EXCLUDE_COL);
+ element.setExcludeList(excludeList);
+ }
+
+ private List<String> getDataList(PowerTableModel p_model, String colName) {
+ String[] dataArray = p_model.getData().getColumn(colName);
+ List<String> list = new LinkedList<String>();
+ for (int i = 0; i < dataArray.length; i++) {
+ list.add(dataArray[i]);
+ }
+ return list;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getStaticLabel() {
+ return "AMF Proxy Server"; // $NON-NLS-1$
+ }
+
+ @Override
+ public String getLabelResource() {
+ return "";
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Collection<String> getMenuCategories() {
+ return Arrays.asList(new String[] { MenuFactory.NON_TEST_ELEMENTS });
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void configure(TestElement element) {
+ log.debug("Configuring gui with " + element);
+ super.configure(element);
+ model = (AmfProxyControl) element;
+ portField.setText(model.getPortString());
+ httpHeaders.setSelected(model.getCaptureHttpHeaders());
+ groupingMode.setSelectedIndex(model.getGroupingMode());
+ addAssertions.setSelected(model.getAssertions());
+ samplerTypeName.setSelectedItem(model.getSamplerTypeName());
+ samplerRedirectAutomatically.setSelected(model.getSamplerRedirectAutomatically());
+ samplerFollowRedirects.setSelected(model.getSamplerFollowRedirects());
+ useKeepAlive.setSelected(model.getUseKeepalive());
+ samplerDownloadImages.setSelected(model.getSamplerDownloadImages());
+ regexMatch.setSelected(model.getRegexMatch());
+ httpsSpoof.setSelected(model.getHttpsSpoof());
+ httpsMatch.setText(model.getHttpsSpoofMatch());
+ httpsMatch.setEnabled(httpsSpoof.isSelected()); // Only valid if Spoof is selected
+ contentTypeInclude.setText(model.getContentTypeInclude());
+ contentTypeExclude.setText(model.getContentTypeExclude());
+
+ reinitializeTargetCombo();// Set up list of potential targets and
+ // enable listener
+
+ populateTable(includeModel, model.getIncludePatterns().iterator());
+ populateTable(excludeModel, model.getExcludePatterns().iterator());
+ repaint();
+ }
+
+ private void populateTable(PowerTableModel p_model, PropertyIterator iter) {
+ p_model.clearData();
+ while (iter.hasNext()) {
+ p_model.addRow(new Object[] { iter.next().getStringValue() });
+ }
+ p_model.fireTableDataChanged();
+ }
+
+ /*
+ * Handles groupingMode. actionPerfomed is not suitable, as that seems to be
+ * activated whenever the Proxy is selected in the Test Plan
+ * Also handles samplerTypeName
+ */
+ /** {@inheritDoc} */
+ public void itemStateChanged(ItemEvent e) {
+ // System.err.println(e.paramString());
+ enableRestart();
+ }
+
+ /** {@inheritDoc} */
+ public void actionPerformed(ActionEvent action) {
+ String command = action.getActionCommand();
+
+ // System.err.println(action.paramString()+" "+command+ "
+ // "+action.getModifiers());
+
+ if (command.equals(STOP)) {
+ model.stopProxy();
+ stop.setEnabled(false);
+ start.setEnabled(true);
+ restart.setEnabled(false);
+ } else if (command.equals(START)) {
+ startProxy();
+ } else if (command.equals(RESTART)) {
+ model.stopProxy();
+ startProxy();
+ } else if (command.equals(ENABLE_RESTART)){
+ enableRestart();
+ httpsMatch.setEnabled(httpsSpoof.isSelected()); // Only valid if Spoof is selected
+ } else if (command.equals(ADD_EXCLUDE)) {
+ excludeModel.addNewRow();
+ excludeModel.fireTableDataChanged();
+ enableRestart();
+ } else if (command.equals(ADD_INCLUDE)) {
+ includeModel.addNewRow();
+ includeModel.fireTableDataChanged();
+ enableRestart();
+ } else if (command.equals(DELETE_EXCLUDE)) {
+ excludeModel.removeRow(excludeTable.getSelectedRow());
+ excludeModel.fireTableDataChanged();
+ enableRestart();
+ } else if (command.equals(DELETE_INCLUDE)) {
+ includeModel.removeRow(includeTable.getSelectedRow());
+ includeModel.fireTableDataChanged();
+ enableRestart();
+ } else if (command.equals(CHANGE_TARGET)) {
+ log.debug("Change target " + targetNodes.getSelectedItem());
+ log.debug("In model " + model);
+ TreeNodeWrapper nw = (TreeNodeWrapper) targetNodes.getSelectedItem();
+ model.setTarget(nw.getTreeNode());
+ enableRestart();
+ }
+ }
+
+ private void startProxy() {
+ ValueReplacer replacer = GuiPackage.getInstance().getReplacer();
+ modifyTestElement(model);
+ try {
+ replacer.replaceValues(model);
+ model.startProxy();
+ start.setEnabled(false);
+ stop.setEnabled(true);
+ restart.setEnabled(false);
+ } catch (InvalidVariableException e) {
+ JOptionPane.showMessageDialog(this,
+ JMeterUtils.getResString("invalid_variables"), // $NON-NLS-1$
+ "Error",
+ JOptionPane.ERROR_MESSAGE);
+ } catch (BindException e) {
+ JOptionPane.showMessageDialog(this,
+ JMeterUtils.getResString("proxy_daemon_bind_error"), // $NON-NLS-1$
+ "Error",
+ JOptionPane.ERROR_MESSAGE);
+ } catch (IOException e) {
+ JOptionPane.showMessageDialog(this,
+ JMeterUtils.getResString("proxy_daemon_error"), // $NON-NLS-1$
+ "Error",
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+
+ private void enableRestart() {
+ if (stop.isEnabled()) {
+ // System.err.println("Enable Restart");
+ restart.setEnabled(true);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public void keyPressed(KeyEvent e) {
+ }
+
+ /** {@inheritDoc} */
+ public void keyTyped(KeyEvent e) {
+ }
+
+ /** {@inheritDoc} */
+ public void keyReleased(KeyEvent e) {
+ String fieldName = e.getComponent().getName();
+
+ if (fieldName.equals(PORTFIELD)) {
+ try {
+ Integer.parseInt(portField.getText());
+ } catch (NumberFormatException nfe) {
+ int length = portField.getText().length();
+ if (length > 0) {
+ JOptionPane.showMessageDialog(this, "Only digits allowed", "Invalid data",
+ JOptionPane.WARNING_MESSAGE);
+ // Drop the last character:
+ portField.setText(portField.getText().substring(0, length-1));
+ }
+ }
+ enableRestart();
+ } else if (fieldName.equals(ENABLE_RESTART)){
+ enableRestart();
+ }
+ }
+
+ private void init() {
+ setLayout(new BorderLayout(0, 5));
+ setBorder(makeBorder());
+
+ add(makeTitlePanel(), BorderLayout.NORTH);
+
+ JPanel mainPanel = new JPanel(new BorderLayout());
+
+ Box myBox = Box.createVerticalBox();
+ myBox.add(createPortPanel());
+ myBox.add(Box.createVerticalStrut(5));
+ myBox.add(createTestPlanContentPanel());
+ myBox.add(Box.createVerticalStrut(5));
+ myBox.add(createHTTPSamplerPanel());
+ myBox.add(Box.createVerticalStrut(5));
+ myBox.add(createContentTypePanel());
+ myBox.add(Box.createVerticalStrut(5));
+ mainPanel.add(myBox, BorderLayout.NORTH);
+
+ Box includeExcludePanel = Box.createVerticalBox();
+ includeExcludePanel.add(createIncludePanel());
+ includeExcludePanel.add(createExcludePanel());
+ mainPanel.add(includeExcludePanel, BorderLayout.CENTER);
+
+ mainPanel.add(createControls(), BorderLayout.SOUTH);
+
+ add(mainPanel, BorderLayout.CENTER);
+ }
+
+ private JPanel createControls() {
+ start = new JButton(JMeterUtils.getResString("start")); // $NON-NLS-1$
+ start.addActionListener(this);
+ start.setActionCommand(START);
+ start.setEnabled(true);
+
+ stop = new JButton(JMeterUtils.getResString("stop")); // $NON-NLS-1$
+ stop.addActionListener(this);
+ stop.setActionCommand(STOP);
+ stop.setEnabled(false);
+
+ restart = new JButton(JMeterUtils.getResString("restart")); // $NON-NLS-1$
+ restart.addActionListener(this);
+ restart.setActionCommand(RESTART);
+ restart.setEnabled(false);
+
+ JPanel panel = new JPanel();
+ panel.add(start);
+ panel.add(stop);
+ panel.add(restart);
+ return panel;
+ }
+
+ private JPanel createPortPanel() {
+ portField = new JTextField(AmfProxyControl.DEFAULT_PORT_S, 5);
+ portField.setName(PORTFIELD);
+ portField.addKeyListener(this);
+
+ JLabel label = new JLabel(JMeterUtils.getResString("port")); // $NON-NLS-1$
+ label.setLabelFor(portField);
+
+ httpsSpoof = new JCheckBox(JMeterUtils.getResString("proxy_httpsspoofing")); // $NON-NLS-1$
+ httpsSpoof.setSelected(false);
+ httpsSpoof.addActionListener(this);
+ httpsSpoof.setActionCommand(ENABLE_RESTART);
+
+ httpsMatch = new JTextField(40);
+ httpsMatch.addKeyListener(this);
+ httpsMatch.setName(ENABLE_RESTART);
+ httpsMatch.setEnabled(false); // Only valid if Spoof is selected
+
+ JLabel matchlabel = new JLabel(JMeterUtils.getResString("proxy_httpsspoofing_match")); // $NON-NLS-1$
+ matchlabel.setLabelFor(httpsMatch);
+
+ HorizontalPanel panel = new HorizontalPanel();
+ panel.add(label);
+ panel.add(portField);
+
+ panel.add(Box.createHorizontalStrut(10));
+ panel.add(httpsSpoof);
+
+ panel.add(matchlabel);
+ panel.add(httpsMatch);
+
+ return panel;
+ }
+
+ private JPanel createTestPlanContentPanel() {
+ httpHeaders = new JCheckBox(JMeterUtils.getResString("proxy_headers")); // $NON-NLS-1$
+ httpHeaders.setSelected(true); // maintain original default
+ httpHeaders.addActionListener(this);
+ httpHeaders.setActionCommand(ENABLE_RESTART);
+
+ addAssertions = new JCheckBox(JMeterUtils.getResString("proxy_assertions")); // $NON-NLS-1$
+ addAssertions.setSelected(false);
+ addAssertions.addActionListener(this);
+ addAssertions.setActionCommand(ENABLE_RESTART);
+
+ regexMatch = new JCheckBox(JMeterUtils.getResString("proxy_regex")); // $NON-NLS-1$
+ regexMatch.setSelected(false);
+ regexMatch.addActionListener(this);
+ regexMatch.setActionCommand(ENABLE_RESTART);
+
+ VerticalPanel mainPanel = new VerticalPanel();
+ mainPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
+ JMeterUtils.getResString("proxy_test_plan_content"))); // $NON-NLS-1$
+
+ HorizontalPanel nodeCreationPanel = new HorizontalPanel();
+ nodeCreationPanel.add(httpHeaders);
+ nodeCreationPanel.add(addAssertions);
+ nodeCreationPanel.add(regexMatch);
+
+ HorizontalPanel targetPanel = new HorizontalPanel();
+ targetPanel.add(createTargetPanel());
+ targetPanel.add(createGroupingPanel());
+ mainPanel.add(targetPanel);
+ mainPanel.add(nodeCreationPanel);
+
+ return mainPanel;
+ }
+
+ private JPanel createHTTPSamplerPanel() {
+ DefaultComboBoxModel m = new DefaultComboBoxModel();
+ m.addElement("AMF"); // TODO: Maybe AMF3/AMF0?
+ samplerTypeName = new JComboBox(m);
+ samplerTypeName.setSelectedIndex(0);
+ samplerTypeName.addItemListener(this);
+ JLabel label2 = new JLabel(JMeterUtils.getResString("proxy_sampler_type")); // $NON-NLS-1$
+ label2.setLabelFor(samplerTypeName);
+
+ samplerRedirectAutomatically = new JCheckBox(JMeterUtils.getResString("follow_redirects_auto")); // $NON-NLS-1$
+ samplerRedirectAutomatically.setSelected(false);
+ samplerRedirectAutomatically.addActionListener(this);
+ samplerRedirectAutomatically.setActionCommand(ENABLE_RESTART);
+
+ samplerFollowRedirects = new JCheckBox(JMeterUtils.getResString("follow_redirects")); // $NON-NLS-1$
+ samplerFollowRedirects.setSelected(true);
+ samplerFollowRedirects.addActionListener(this);
+ samplerFollowRedirects.setActionCommand(ENABLE_RESTART);
+
+ useKeepAlive = new JCheckBox(JMeterUtils.getResString("use_keepalive")); // $NON-NLS-1$
+ useKeepAlive.setSelected(true);
+ useKeepAlive.addActionListener(this);
+ useKeepAlive.setActionCommand(ENABLE_RESTART);
+
+ samplerDownloadImages = new JCheckBox(JMeterUtils.getResString("web_testing_retrieve_images")); // $NON-NLS-1$
+ samplerDownloadImages.setSelected(false);
+ samplerDownloadImages.addActionListener(this);
+ samplerDownloadImages.setActionCommand(ENABLE_RESTART);
+
+ HorizontalPanel panel = new HorizontalPanel();
+ panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
+ JMeterUtils.getResString("proxy_sampler_settings"))); // $NON-NLS-1$
+ panel.add(label2);
+ panel.add(samplerTypeName);
+ panel.add(samplerRedirectAutomatically);
+ panel.add(samplerFollowRedirects);
+ panel.add(useKeepAlive);
+ panel.add(samplerDownloadImages);
+
+ return panel;
+ }
+
+ private JPanel createTargetPanel() {
+ targetNodesModel = new DefaultComboBoxModel();
+ targetNodes = new JComboBox(targetNodesModel);
+ targetNodes.setActionCommand(CHANGE_TARGET);
+ // Action listener will be added later
+
+ JLabel label = new JLabel(JMeterUtils.getResString("proxy_target")); // $NON-NLS-1$
+ label.setLabelFor(targetNodes);
+
+ HorizontalPanel panel = new HorizontalPanel();
+ panel.add(label);
+ panel.add(targetNodes);
+
+ return panel;
+ }
+
+ private JPanel createGroupingPanel() {
+ DefaultComboBoxModel m = new DefaultComboBoxModel();
+ // Note: position of these elements in the menu *must* match the
+ // corresponding ProxyControl.GROUPING_* values.
+ m.addElement(JMeterUtils.getResString("grouping_no_groups")); // $NON-NLS-1$
+ m.addElement(JMeterUtils.getResString("grouping_add_separators")); // $NON-NLS-1$
+ m.addElement(JMeterUtils.getResString("grouping_in_controllers")); // $NON-NLS-1$
+ m.addElement(JMeterUtils.getResString("grouping_store_first_only")); // $NON-NLS-1$
+ m.addElement(JMeterUtils.getResString("grouping_in_transaction_controllers")); // $NON-NLS-1$
+ groupingMode = new JComboBox(m);
+ groupingMode.setSelectedIndex(0);
+ groupingMode.addItemListener(this);
+
+ JLabel label2 = new JLabel(JMeterUtils.getResString("grouping_mode")); // $NON-NLS-1$
+ label2.setLabelFor(groupingMode);
+
+ HorizontalPanel panel = new HorizontalPanel();
+ panel.add(label2);
+ panel.add(groupingMode);
+
+ return panel;
+ }
+
+ private JPanel createContentTypePanel() {
+ contentTypeInclude = new JTextField(35);
+ contentTypeInclude.addKeyListener(this);
+ contentTypeInclude.setName(ENABLE_RESTART);
+ JLabel labelInclude = new JLabel(JMeterUtils.getResString("proxy_content_type_include")); // $NON-NLS-1$
+ labelInclude.setLabelFor(contentTypeInclude);
+ // Default value
+ contentTypeInclude.setText(JMeterUtils.getProperty("proxy.content_type_include")); // $NON-NLS-1$
+
+ contentTypeExclude = new JTextField(35);
+ contentTypeExclude.addKeyListener(this);
+ contentTypeExclude.setName(ENABLE_RESTART);
+ JLabel labelExclude = new JLabel(JMeterUtils.getResString("proxy_content_type_exclude")); // $NON-NLS-1$
+ labelExclude.setLabelFor(contentTypeExclude);
+ // Default value
+ contentTypeExclude.setText(JMeterUtils.getProperty("proxy.content_type_exclude")); // $NON-NLS-1$
+
+ HorizontalPanel panel = new HorizontalPanel();
+ panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
+ JMeterUtils.getResString("proxy_content_type_filter"))); // $NON-NLS-1$
+ panel.add(labelInclude);
+ panel.add(contentTypeInclude);
+ panel.add(labelExclude);
+ panel.add(contentTypeExclude);
+
+ return panel;
+ }
+
+ private JPanel createIncludePanel() {
+ includeModel = new PowerTableModel(new String[] { INCLUDE_COL }, new Class[] { String.class });
+ includeTable = new JTable(includeModel);
+ includeTable.getTableHeader().setDefaultRenderer(new HeaderAsPropertyRenderer());
+ includeTable.setPreferredScrollableViewportSize(new Dimension(100, 30));
+
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), JMeterUtils
+ .getResString("patterns_to_include"))); // $NON-NLS-1$
+
+ panel.add(new JScrollPane(includeTable), BorderLayout.CENTER);
+ panel.add(createTableButtonPanel(ADD_INCLUDE, DELETE_INCLUDE), BorderLayout.SOUTH);
+
+ return panel;
+ }
+
+ private JPanel createExcludePanel() {
+ excludeModel = new PowerTableModel(new String[] { EXCLUDE_COL }, new Class[] { String.class });
+ excludeTable = new JTable(excludeModel);
+ excludeTable.getTableHeader().setDefaultRenderer(new HeaderAsPropertyRenderer());
+ excludeTable.setPreferredScrollableViewportSize(new Dimension(100, 30));
+
+ JPanel panel = new JPanel(new BorderLayout());
+ panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), JMeterUtils
+ .getResString("patterns_to_exclude"))); // $NON-NLS-1$
+
+ panel.add(new JScrollPane(excludeTable), BorderLayout.CENTER);
+ panel.add(createTableButtonPanel(ADD_EXCLUDE, DELETE_EXCLUDE), BorderLayout.SOUTH);
+
+ return panel;
+ }
+
+ private JPanel createTableButtonPanel(String addCommand, String deleteCommand) {
+ JPanel buttonPanel = new JPanel();
+
+ JButton addButton = new JButton(JMeterUtils.getResString("add")); // $NON-NLS-1$
+ addButton.setActionCommand(addCommand);
+ addButton.addActionListener(this);
+ buttonPanel.add(addButton);
+
+ JButton deleteButton = new JButton(JMeterUtils.getResString("delete")); // $NON-NLS-1$
+ deleteButton.setActionCommand(deleteCommand);
+ deleteButton.addActionListener(this);
+ buttonPanel.add(deleteButton);
+
+ return buttonPanel;
+ }
+
+ private void reinitializeTargetCombo() {
+ log.debug("Reinitializing target combo");
+
+ // Stop action notifications while we shuffle this around:
+ targetNodes.removeActionListener(this);
+
+ targetNodesModel.removeAllElements();
+ GuiPackage gp = GuiPackage.getInstance();
+ JMeterTreeNode root;
+ if (gp != null) {
+ root = (JMeterTreeNode) GuiPackage.getInstance().getTreeModel().getRoot();
+ targetNodesModel
+ .addElement(new TreeNodeWrapper(null, JMeterUtils.getResString("use_recording_controller"))); // $NON-NLS-1$
+ buildNodesModel(root, "", 0);
+ }
+ TreeNodeWrapper choice = null;
+ for (int i = 0; i < targetNodesModel.getSize(); i++) {
+ choice = (TreeNodeWrapper) targetNodesModel.getElementAt(i);
+ log.debug("Selecting item " + choice + " for model " + model + " in " + this);
+ if (choice.getTreeNode() == model.getTarget()) // .equals caused
+ // NPE
+ {
+ break;
+ }
+ }
+ // Reinstate action notifications:
+ targetNodes.addActionListener(this);
+ // Set the current value:
+ targetNodesModel.setSelectedItem(choice);
+
+ log.debug("Reinitialization complete");
+ }
+
+ private void buildNodesModel(JMeterTreeNode node, String parent_name, int level) {
+ String seperator = " > ";
+ if (node != null) {
+ for (int i = 0; i < node.getChildCount(); i++) {
+ StringBuilder name = new StringBuilder();
+ JMeterTreeNode cur = (JMeterTreeNode) node.getChildAt(i);
+ TestElement te = cur.getTestElement();
+ /*
+ * Will never be true. Probably intended to use
+ * org.apache.jmeter.threads.ThreadGroup rather than
+ * java.lang.ThreadGroup However, that does not work correctly;
+ * whereas treating it as a Controller does. if (te instanceof
+ * ThreadGroup) { name.append(parent_name);
+ * name.append(cur.getName()); name.append(seperator);
+ * buildNodesModel(cur, name.toString(), level); } else
+ */
+ if (te instanceof Controller) {
+ name.append(spaces(level));
+ name.append(parent_name);
+ name.append(cur.getName());
+ TreeNodeWrapper tnw = new TreeNodeWrapper(cur, name.toString());
+ targetNodesModel.addElement(tnw);
+ name = new StringBuilder();
+ name.append(cur.getName());
+ name.append(seperator);
+ buildNodesModel(cur, name.toString(), level + 1);
+ } else if (te instanceof TestPlan || te instanceof WorkBench) {
+ name.append(cur.getName());
+ name.append(seperator);
+ buildNodesModel(cur, name.toString(), 0);
+ }
+ // Ignore everything else
+ }
+ }
+ }
+
+ private String spaces(int level) {
+ int multi = 4;
+ StringBuilder spaces = new StringBuilder(level * multi);
+ for (int i = 0; i < level * multi; i++) {
+ spaces.append(" "); // $NON-NLS-1$
+ }
+ return spaces.toString();
+ }
+
+}
+
+class TreeNodeWrapper {
+ private final JMeterTreeNode tn;
+
+ private final String label;
+
+ public TreeNodeWrapper(JMeterTreeNode tn, String label) {
+ this.tn = tn;
+ this.label = label;
+ }
+
+ public JMeterTreeNode getTreeNode() {
+ return tn;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public String toString() {
+ return label;
+ }
+}
View
204 src/protocol/amf/org/apache/jmeter/protocol/amf/gui/AmfRequestDefaultsGui.java
@@ -0,0 +1,204 @@
+/*
+* Copyright 2011 the original author or authors.
+*
+* Licensed 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.amf.gui;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import org.apache.jmeter.config.Arguments;
+import org.apache.jmeter.config.ConfigTestElement;
+import org.apache.jmeter.config.gui.AbstractConfigGui;
+import org.apache.jmeter.config.gui.ArgumentsPanel;
+import org.apache.jmeter.gui.util.HorizontalPanel;
+import org.apache.jmeter.gui.util.VerticalPanel;
+import org.apache.jmeter.protocol.amf.sampler.AmfRequest;
+import org.apache.jmeter.protocol.http.config.gui.UrlConfigGui;
+import org.apache.jmeter.testelement.AbstractTestElement;
+import org.apache.jmeter.testelement.TestElement;
+import org.apache.jmeter.testelement.property.JMeterProperty;
+import org.apache.jmeter.testelement.property.TestElementProperty;
+import org.apache.jorphan.logging.LoggingManager;
+import org.apache.log.Logger;
+
+import flex.messaging.io.MessageIOConstants;
+
+/**
+ * JMeter configuration GUI component that provides configuration support
+ * for the AmfSampler.
+ *
+ */
+public class AmfRequestDefaultsGui extends AbstractConfigGui implements ActionListener {
+
+ private static final long serialVersionUID = 1L;
+
+ private static final Logger log = LoggingManager.getLoggerForClass();
+
+ private UrlConfigGui urlConfigGui;
+
+ private JComboBox objectEncodingCombo;
+
+ private ArgumentsPanel propertyOverrides;
+
+ public AmfRequestDefaultsGui() {
+ init();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ private void init() {
+ setLayout(new BorderLayout(0, 5));
+ setBorder(makeBorder());
+
+ add(makeTitlePanel(), BorderLayout.NORTH);
+
+ VerticalPanel centerPanel = new VerticalPanel();
+
+ centerPanel.add(getAmfRequestPanel());
+
+ urlConfigGui = new UrlConfigGui(false, false);
+ centerPanel.add(urlConfigGui);
+
+ add(centerPanel, BorderLayout.CENTER);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getStaticLabel() {
+ return "AMF Request Defaults"; // $NON-NLS-1$
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getLabelResource() {
+ return "";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void configure(TestElement element) {
+ super.configure(element);
+ urlConfigGui.clear();
+
+ // Configure AMF request specific properties
+ objectEncodingCombo.setSelectedItem(element.getPropertyAsString(AmfRequest.OBJECT_ENCODING_VERSION));
+
+ final JMeterProperty po = element.getProperty(AmfRequest.PROPERTY_OVERRIDES);
+ if (po != null && po.getObjectValue() != null) {
+ propertyOverrides.configure((Arguments) po.getObjectValue());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public TestElement createTestElement() {
+ ConfigTestElement element = new ConfigTestElement();
+ element.setName(this.getName());
+ element.setProperty(TestElement.GUI_CLASS, this.getClass().getName());
+ element.setProperty(TestElement.TEST_CLASS, element.getClass().getName());
+ modifyTestElement(element);
+ return element;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void modifyTestElement(TestElement element) {
+ ConfigTestElement cfg = (ConfigTestElement) element;
+ ConfigTestElement urlEl = (ConfigTestElement) urlConfigGui.createTestElement();
+ cfg.clear();
+ cfg.addConfigElement(urlEl);
+ super.configureTestElement(element);
+
+ // Set AMF properties
+ element.setProperty(AmfRequest.OBJECT_ENCODING_VERSION, String.valueOf(objectEncodingCombo.getSelectedItem()));
+
+ element.setProperty(new TestElementProperty(AmfRequest.PROPERTY_OVERRIDES, (Arguments) propertyOverrides.createTestElement()));
+ }
+
+ public void clear() {
+ urlConfigGui.clear();
+ propertyOverrides.clear();
+ }
+
+ protected final JPanel getAmfRequestPanel() {
+ JPanel panel = new JPanel();
+ panel.setLayout(new BorderLayout());
+ panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
+ "AMF Request Defaults")); // $NON-NLS-1$
+
+ panel.add(getObjectEncodingPanel(), BorderLayout.NORTH);
+
+ panel.add(getPropertyOverridesPanel(), BorderLayout.SOUTH);
+
+ return panel;
+ }
+
+ protected final JPanel getObjectEncodingPanel() {
+ JPanel panel = new JPanel();
+ panel.setLayout(new BorderLayout());
+ panel.add(getObjectEncodingVersionPanel(), BorderLayout.NORTH);
+ return panel;
+ }
+
+ private JPanel getObjectEncodingVersionPanel() {
+
+ List<String> values = new ArrayList<String>();
+ values.add("AMF"+String.valueOf(MessageIOConstants.AMF3));
+
+ JLabel label = new JLabel("AMF Encoding"); // $NON-NLS-1$
+
+ objectEncodingCombo = new JComboBox(values.toArray());
+ objectEncodingCombo.setEditable(false);
+ label.setLabelFor(objectEncodingCombo);
+
+ HorizontalPanel panel = new HorizontalPanel();
+ panel.add(label);
+ panel.add(objectEncodingCombo);
+
+ return panel;
+ }
+
+ private final JPanel getPropertyOverridesPanel() {
+ propertyOverrides = new ArgumentsPanel("Value Replacement (name: search string, value: replace string)");
+
+ return propertyOverrides;
+ }
+
+ public void actionPerformed(ActionEvent evt) {
+ //if (evt.getSource() == objectEncodingCombo) {
+ //}
+ }
+
+}
View
188 src/protocol/amf/org/apache/jmeter/protocol/amf/gui/AmfRequestGui.java
@@ -0,0 +1,188 @@
+/*
+* Copyright 2011 the original author or authors.
+*
+* Licensed 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.amf.gui;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.BorderFactory;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import org.apache.jmeter.gui.util.HorizontalPanel;
+import org.apache.jmeter.gui.util.VerticalPanel;
+import org.apache.jmeter.protocol.amf.sampler.AmfRequest;
+import org.apache.jmeter.protocol.amf.sampler.AmfRequestFactory;
+import org.apache.jmeter.protocol.http.config.gui.UrlConfigGui;
+import org.apache.jmeter.samplers.gui.AbstractSamplerGui;
+import org.apache.jmeter.testelement.TestElement;
+import org.apache.jorphan.gui.JLabeledTextArea;
+
+import flex.messaging.io.MessageIOConstants;
+
+/**
+ * The JMeter GUI component which manage the AmfSampler.
+ *
+ */
+public class AmfRequestGui extends AbstractSamplerGui {
+
+ private static final long serialVersionUID = 1L;
+
+ private UrlConfigGui urlConfigGui;
+
+ private JComboBox objectEncodingCombo;
+
+ private JLabeledTextArea amfXml;
+
+ public AmfRequestGui() {
+ init();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getStaticLabel() {
+ return "AMF Request"; // $NON-NLS-1$
+ }
+
+ @Override
+ public String getLabelResource() {
+ return "";
+ }
+
+ private void init() {
+ setLayout(new BorderLayout(0, 5));
+ setBorder(makeBorder());
+
+ add(makeTitlePanel(), BorderLayout.NORTH);
+
+ VerticalPanel centerPanel = new VerticalPanel();
+
+ centerPanel.add(getAmfRequestPanel());
+
+ // TODO: Some sort of accordian to shrink the URL config area
+ urlConfigGui = new UrlConfigGui(false, false);
+ centerPanel.add(urlConfigGui);
+
+ add(centerPanel, BorderLayout.CENTER);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void configure(TestElement element) {
+ super.configure(element);
+ urlConfigGui.configure(element);
+
+ objectEncodingCombo.setSelectedItem(element.getPropertyAsString(AmfRequest.OBJECT_ENCODING_VERSION));
+ amfXml.setText(element.getPropertyAsString(AmfRequest.AMFXML));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public TestElement createTestElement() {
+ AmfRequest sampler = AmfRequestFactory.newInstance();// create default sampler
+ modifyTestElement(sampler);
+ return sampler;
+ }
+
+ /**
+ * Modifies a given TestElement to mirror the data in the gui components.
+ * <p>
+ * {@inheritDoc}
+ */
+ public void modifyTestElement(TestElement element) {
+ element.clear();
+ urlConfigGui.modifyTestElement(element);
+ super.configureTestElement(element);
+
+ element.setProperty(AmfRequest.OBJECT_ENCODING_VERSION, String.valueOf(objectEncodingCombo.getSelectedItem()));
+ element.setProperty(AmfRequest.AMFXML, amfXml.getText(), "");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Dimension getPreferredSize() {
+ return getMinimumSize();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void clearGui() {
+ super.clearGui();
+ urlConfigGui.clear();
+ amfXml.setText("");
+ }
+
+
+ protected final JPanel getAmfRequestPanel() {
+ JPanel panel = new JPanel();
+ panel.setLayout(new BorderLayout());
+ panel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
+ "AMF Request")); // $NON-NLS-1$
+
+ panel.add(getObjectEncodingPanel(), BorderLayout.NORTH);
+ panel.add(getAmfXmlPanel(), BorderLayout.CENTER);
+
+ return panel;
+ }
+
+ protected JPanel getAmfXmlPanel() {
+ amfXml = new JLabeledTextArea("XML Representation"); // $NON-NLS-1$
+ return amfXml;
+ }
+
+ protected final JPanel getObjectEncodingPanel() {
+ JPanel panel = new JPanel();
+ panel.setLayout(new BorderLayout());
+ panel.add(getObjectEncodingVersionPanel(), BorderLayout.NORTH);
+ return panel;
+ }
+
+ /**
+ * Create a panel with GUI components allowing the user to select an
+ * AMF Object Encoding Version.
+ *
+ * @return a panel containing the relevant components
+ */
+ protected JPanel getObjectEncodingVersionPanel() {
+
+ List<String> values = new ArrayList<String>();
+ values.add("AMF"+String.valueOf(MessageIOConstants.AMF3));
+
+ JLabel label = new JLabel("AMF Encoding"); // $NON-NLS-1$
+
+ objectEncodingCombo = new JComboBox(values.toArray());
+ objectEncodingCombo.setEditable(false);
+ label.setLabelFor(objectEncodingCombo);
+
+ HorizontalPanel panel = new HorizontalPanel();
+ panel.add(label);
+ panel.add(objectEncodingCombo);
+
+ return panel;
+ }
+}
View
155 src/protocol/amf/org/apache/jmeter/protocol/amf/proxy/AmfDaemon.java
@@ -0,0 +1,155 @@
+/*
+* Copyright 2011 the original author or authors.
+*
+* Licensed 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.amf.proxy;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.jorphan.logging.LoggingManager;
+import org.apache.jorphan.util.JOrphanUtils;
+import org.apache.log.Logger;
+
+/**
+ * Web daemon thread. Creates main socket on port 8080 and listens on it
+ * forever. For each client request, creates a proxy thread to handle the
+ * request.
+ *
+ */
+public class AmfDaemon extends Thread {
+
+ private static final Logger log = LoggingManager.getLoggerForClass();
+
+ /**
+ * The time (in milliseconds) to wait when accepting a client connection.
+ * The accept will be retried until the Daemon is told to stop. So this
+ * interval is the longest time that the Daemon will have to wait after
+ * being told to stop.
+ */
+ private static final int ACCEPT_TIMEOUT = 1000;
+
+ /** The port to listen on. */
+ private final int daemonPort;
+
+ private final ServerSocket mainSocket;
+
+ /** True if the Daemon is currently running. */
+ private volatile boolean running;
+
+ /** The target which will receive the generated JMeter test components. */
+ private final AmfProxyControl target;
+
+ /**
+ * The proxy class which will be used to handle individual requests. This
+ * class must be the {@link Proxy} class or a subclass.
+ */
+ private final Class<? extends AmfProxy> proxyClass;
+
+ /**
+ * Create a new Daemon with the specified port and target.
+ *
+ * @param port
+ * the port to listen on.
+ * @param target
+ * the target which will receive the generated JMeter test
+ * components.
+ * @throws IOException
+ */
+ public AmfDaemon(int port, AmfProxyControl target) throws IOException {
+ this(port, target, AmfProxy.class);
+ }
+
+ /**
+ * Create a new Daemon with the specified port and target, using the
+ * specified class to handle individual requests.
+ *
+ * @param port
+ * the port to listen on.
+ * @param target
+ * the target which will receive the generated JMeter test
+ * components.
+ * @param proxyClass
+ * the proxy class to use to handle individual requests. This
+ * class must be the {@link Proxy} class or a subclass.
+ * @throws IOException
+ */
+ public AmfDaemon(int port, AmfProxyControl target, Class<? extends AmfProxy> proxyClass) throws IOException {
+ super("HTTP Proxy Daemon");
+ this.target = target;
+ this.daemonPort = port;
+ this.proxyClass = proxyClass;
+ log.info("Creating Daemon Socket on port: " + daemonPort);
+ mainSocket = new ServerSocket(daemonPort);
+ mainSocket.setSoTimeout(ACCEPT_TIMEOUT);
+ }
+
+ /**
+ * Listen on the daemon port and handle incoming requests. This method will
+ * not exit until {@link #stopServer()} is called or an error occurs.
+ */
+ @Override
+ public void run() {
+ running = true;
+ log.info("Proxy up and running!");
+
+ // Maps to contain page and form encodings
+ // TODO - do these really need to be shared between all Proxy instances?
+ Map<String, String> pageEncodings = Collections.synchronizedMap(new HashMap<String, String>());
+ Map<String, String> formEncodings = Collections.synchronizedMap(new HashMap<String, String>());
+
+ try {
+ while (running) {
+ try {
+ // Listen on main socket
+ Socket clientSocket = mainSocket.accept();
+ if (running) {
+ // Pass request to new proxy thread
+ AmfProxy thd = proxyClass.newInstance();
+ thd.configure(clientSocket, target, pageEncodings, formEncodings);
+ thd.start();
+ }
+ } catch (InterruptedIOException e) {
+ continue;
+ // Timeout occurred. Ignore, and keep looping until we're
+ // told to stop running.
+ }
+ }
+ log.info("Proxy Server stopped");
+ } catch (Exception e) {
+ log.warn("Proxy Server stopped", e);
+ } finally {
+ JOrphanUtils.closeQuietly(mainSocket);
+ }
+
+ // Clear maps
+ pageEncodings = null;
+ formEncodings = null;
+ }
+
+ /**
+ * Stop the proxy daemon. The daemon may not stop immediately.
+ *
+ * see #ACCEPT_TIMEOUT
+ */
+ public void stopServer() {
+ running = false;
+ }
+}
View
580 src/protocol/amf/org/apache/jmeter/protocol/amf/proxy/AmfProxy.java
@@ -0,0 +1,580 @@
+/*
+* Copyright 2011 the original author or authors.
+*
+* Licensed 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.amf.proxy;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.jmeter.protocol.amf.sampler.AmfRequest;
+import org.apache.jmeter.protocol.amf.sampler.AmfRequestFactory;
+import org.apache.jmeter.protocol.amf.util.AmfXmlConverter;
+import org.apache.jmeter.protocol.http.control.HeaderManager;
+import org.apache.jmeter.protocol.http.parser.HTMLParseException;
+import org.apache.jmeter.protocol.http.proxy.Proxy;
+import org.apache.jmeter.protocol.http.proxy.ProxyControl;
+import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase;
+import org.apache.jmeter.protocol.http.sampler.HTTPSamplerFactory;
+import org.apache.jmeter.protocol.http.util.ConversionUtils;
+import org.apache.jmeter.protocol.http.util.HTTPConstants;
+import org.apache.jmeter.samplers.SampleResult;
+import org.apache.jmeter.testelement.TestElement;
+import org.apache.jmeter.util.JMeterUtils;
+import org.apache.jorphan.logging.LoggingManager;
+import org.apache.jorphan.util.JOrphanUtils;
+import org.apache.log.Logger;
+
+/**
+ * Thread to handle one client request. Gets the request from the client and
+ * passes it on to the server, then sends the response back to the client.
+ * Information about the request and response is stored so it can be used in a
+ * JMeter test plan.
+ *
+ */
+public class AmfProxy extends Thread {
+ private static final Logger log = LoggingManager.getLoggerForClass();
+
+ private static final byte[] CRLF_BYTES = { 0x0d, 0x0a };
+ private static final String CRLF_STRING = "\r\n";
+
+ private static final String NEW_LINE = "\n"; // $NON-NLS-1$
+
+ private static final String[] headersToRemove;
+
+ // Allow list of headers to be overridden
+ private static final String PROXY_HEADERS_REMOVE = "proxy.headers.remove"; // $NON-NLS-1$
+
+ private static final String PROXY_HEADERS_REMOVE_DEFAULT = "If-Modified-Since,If-None-Match,Host"; // $NON-NLS-1$
+
+ private static final String PROXY_HEADERS_REMOVE_SEPARATOR = ","; // $NON-NLS-1$
+
+ // for ssl connection
+ private static final String KEYSTORE_TYPE =
+ JMeterUtils.getPropDefault("proxy.cert.type", "JKS"); // $NON-NLS-1$ $NON-NLS-2$
+
+ private static final String KEYMANAGERFACTORY =
+ JMeterUtils.getPropDefault("proxy.cert.factory", "SunX509"); // $NON-NLS-1$ $NON-NLS-2$
+
+ private static final String SSLCONTEXT_PROTOCOL =
+ JMeterUtils.getPropDefault("proxy.ssl.protocol", "SSLv3"); // $NON-NLS-1$ $NON-NLS-2$
+
+ // HashMap to save ssl connection between Jmeter proxy and browser
+ private static final HashMap<String, SSLSocketFactory> hashHost = new HashMap<String, SSLSocketFactory>();
+
+ // Proxy configuration SSL
+ private static final String CERT_DIRECTORY =
+ JMeterUtils.getPropDefault("proxy.cert.directory", JMeterUtils.getJMeterBinDir()); // $NON-NLS-1$
+
+ private static final String CERT_FILE_DEFAULT = "proxyserver.jks";// $NON-NLS-1$
+
+ private static final String CERT_FILE =
+ JMeterUtils.getPropDefault("proxy.cert.file", CERT_FILE_DEFAULT); // $NON-NLS-1$
+
+ private static final char[] KEYSTORE_PASSWORD =
+ JMeterUtils.getPropDefault("proxy.cert.keystorepass", "password").toCharArray(); // $NON-NLS-1$ $NON-NLS-2$
+
+ private static final char[] KEY_PASSWORD =
+ JMeterUtils.getPropDefault("proxy.cert.keypassword","password").toCharArray(); // $NON-NLS-1$ $NON-NLS-2$
+
+ // Use with SSL connection
+ private OutputStream outStreamClient = null;
+
+ static {
+ String removeList = JMeterUtils.getPropDefault(PROXY_HEADERS_REMOVE,PROXY_HEADERS_REMOVE_DEFAULT);
+ headersToRemove = JOrphanUtils.split(removeList,PROXY_HEADERS_REMOVE_SEPARATOR);
+ log.info("Proxy will remove the headers: "+removeList);
+ }
+
+ /** Socket to client. */
+ private Socket clientSocket = null;
+
+ /** Target to receive the generated sampler. */
+ private AmfProxyControl target;
+
+ /** Whether or not to capture the HTTP headers. */
+ private boolean captureHttpHeaders;
+
+ /** Whether to try to spoof as https **/
+ private boolean httpsSpoof;
+
+ private String httpsSpoofMatch; // if non-empty, then URLs must match in order to be spoofed
+
+ /** Reference to Deamon's Map of url string to page character encoding of that page */
+ private Map<String, String> pageEncodings;
+ /** Reference to Deamon's Map of url string to character encoding for the form */
+ private Map<String, String> formEncodings;
+
+ /**
+ * Default constructor - used by newInstance call in Daemon
+ */
+ public AmfProxy() {
+ //super();
+ }
+
+ /**
+ * Configure the Proxy.
+ * Intended to be called directly after construction.
+ * Should not be called after it has been passed to a new thread,
+ * otherwise the variables may not be published correctly.
+ *
+ * @param _clientSocket
+ * the socket connection to the client
+ * @param _target
+ * the ProxyControl which will receive the generated sampler
+ * @param _pageEncodings
+ * reference to the Map of Deamon, with mappings from page urls to encoding used
+ * @param formEncodingsEncodings
+ * reference to the Map of Deamon, with mappings from form action urls to encoding used
+ */
+ void configure(Socket _clientSocket, AmfProxyControl _target, Map<String, String> _pageEncodings, Map<String, String> _formEncodings) {
+ this.target = _target;
+ this.clientSocket = _clientSocket;
+ this.captureHttpHeaders = _target.getCaptureHttpHeaders();
+ this.httpsSpoof = _target.getHttpsSpoof();
+ this.httpsSpoofMatch = _target.getHttpsSpoofMatch();
+ this.pageEncodings = _pageEncodings;
+ this.formEncodings = _formEncodings;
+ }
+
+ /**
+ * Main processing method for the Proxy object
+ */
+ @Override
+ public void run() {
+ // Instantiate the sampler
+ // TODO: HTTPSamplers for non-amf requests
+ HTTPSamplerBase sampler = AmfRequestFactory.newInstance();
+
+ AmfRequestHdr request = new AmfRequestHdr(sampler);
+ SampleResult result = null;
+ HeaderManager headers = null;
+
+ try {
+ // Parse only first line
+ request.parse(new BufferedInputStream(clientSocket.getInputStream()));
+ outStreamClient = clientSocket.getOutputStream();
+
+ if ((request.getMethod().startsWith(HTTPConstants.CONNECT)) && (outStreamClient != null)) {
+ log.debug("Method CONNECT => SSL");
+ // write a OK reponse to browser, to engage SSL exchange
+ outStreamClient.write(("HTTP/1.0 200 OK\r\n\r\n").getBytes()); // $NON-NLS-1$ // TODO charset?
+ outStreamClient.flush();
+ // With ssl request, url is host:port (without https:// or path)
+ String[] param = request.getUrl().split(":"); // $NON-NLS-1$
+ if (param.length == 2) {
+ log.debug("Start to negotiate SSL connection, host: " + param[0]);
+ clientSocket = startSSL(clientSocket, param[0]);
+ } else {
+ log.warn("In SSL request, unable to find host and port in CONNECT request");
+ }
+ // Re-parse (now it's the http request over SSL)
+ request.parse(new BufferedInputStream(clientSocket.getInputStream()));
+ }
+
+
+ // Populate the sampler. It is the same sampler as we sent into
+ // the constructor of the HttpRequestHdr instance above
+ request.getSampler(pageEncodings, formEncodings);
+
+ /*
+ * Create a Header Manager to ensure that the browsers headers are
+ * captured and sent to the server
+ */
+ headers = request.getHeaderManager();
+ sampler.setHeaderManager(headers);
+
+ /*
+ * If we are trying to spoof https, change the protocol
+ */
+ boolean forcedHTTPS = false; // so we know when to revert
+ if (httpsSpoof) {
+ if (httpsSpoofMatch.length() > 0){
+ String url = request.getUrl();
+ if (url.matches(httpsSpoofMatch)){
+ sampler.setProtocol(HTTPConstants.PROTOCOL_HTTPS);
+ forcedHTTPS = true;
+ }
+ } else {
+ sampler.setProtocol(HTTPConstants.PROTOCOL_HTTPS);
+ forcedHTTPS = true;
+ }
+ }
+ sampler.threadStarted(); // Needed for HTTPSampler2
+ result = sampler.sample();
+
+ /*
+ * If we're dealing with text data, and if we're spoofing https,
+ * replace all occurences of "https://" with "http://" for the client.
+ * TODO - also check the match string to restrict the changes further?
+ */
+ if (httpsSpoof && SampleResult.TEXT.equals(result.getDataType()))
+ {
+ final String enc = result.getDataEncodingWithDefault();
+ String noHttpsResult = new String(result.getResponseData(),enc);
+ final String HTTPS_HOST = // match https://host[:port]/ and drop default port if present
+ "https://([^:/]+)(:"+HTTPConstants.DEFAULT_HTTPS_PORT_STRING+")?"; // $NON-NLS-1$ $NON-NLS-2$
+ noHttpsResult = noHttpsResult.replaceAll(HTTPS_HOST, "http://$1"); // $NON-NLS-1$
+ result.setResponseData(noHttpsResult.getBytes(enc));
+ }
+
+ // Find the page encoding and possibly encodings for forms in the page
+ // in the response from the web server
+ String pageEncoding = addPageEncoding(result);
+ addFormEncodings(result, pageEncoding);
+
+ writeToClient(result, new BufferedOutputStream(clientSocket.getOutputStream()), forcedHTTPS);
+ } catch (UnknownHostException uhe) {
+ log.warn("Server Not Found.", uhe);
+ writeErrorToClient(HttpReplyHdr.formServerNotFound());
+ result = generateErrorResult(result, uhe); // Generate result (if nec.) and populate it
+ } catch (IllegalArgumentException e) {
+ log.error("Not implemented (probably used https)", e);
+ writeErrorToClient(HttpReplyHdr.formNotImplemented("Probably used https instead of http. " +
+ "To record https requests, see " +
+ "<a href=\"http://jakarta.apache.org/jmeter/usermanual/component_reference.html#HTTP_Proxy_Server\">HTTP Proxy Server documentation</a>"));
+ result = generateErrorResult(result, e); // Generate result (if nec.) and populate it
+ } catch (IOException ioe) {
+ log.error("Problem with SSL certificate? Ensure browser is set to accept the JMeter proxy cert: "+ioe.getLocalizedMessage());
+ // won't work: writeErrorToClient(HttpReplyHdr.formInternalError());
+ if (result == null) {
+ result = new SampleResult();
+ result.setSampleLabel("Sample failed");
+ }
+ result.setResponseMessage(ioe.getMessage()+ "\n**ensure browser is set to accept the JMeter proxy certificate**");
+ } catch (Exception e) {
+ log.error("Exception when processing sample", e);
+ writeErrorToClient(HttpReplyHdr.formInternalError());
+ result = generateErrorResult(result, e); // Generate result (if nec.) and populate it
+ } finally {
+ if (log.isDebugEnabled()) {
+ log.debug("Will deliver sample " + sampler.getName());
+ }
+ /*
+ * We don't want to store any cookies in the generated test plan
+ */
+ if (headers != null) {
+ headers.removeHeaderNamed(HTTPConstants.HEADER_COOKIE);// Always remove cookies
+ headers.removeHeaderNamed(HTTPConstants.HEADER_AUTHORIZATION);// Always remove authorization
+ // Remove additional headers
+ for(int i=0; i < headersToRemove.length; i++){
+ headers.removeHeaderNamed(headersToRemove[i]);
+ }
+ }
+ target.deliverSampler(sampler, new TestElement[] { captureHttpHeaders ? headers : null }, result);
+ try {
+ clientSocket.close();
+ } catch (Exception e) {
+ log.error("", e);
+ }
+ sampler.threadFinished(); // Needed for HTTPSampler2
+ }
+ }
+
+ /**
+ * Get SSL connection from hashmap, creating it if necessary.
+ *
+ * @param host
+ * @return a ssl socket factory
+ * @throws IOException
+ */
+ private SSLSocketFactory getSSLSocketFactory(String host) throws IOException {
+ synchronized (hashHost) {
+ if (hashHost.containsKey(host)) {
+ log.debug("Good, already in map, host=" + host);
+ return hashHost.get(host);
+ }
+ InputStream in = getCertificate();
+ Exception except = null;
+ if (in != null) {
+ KeyStore ks = null;
+ KeyManagerFactory kmf = null;
+ SSLContext sslcontext = null;
+ try {
+ ks = KeyStore.getInstance(KEYSTORE_TYPE);
+ ks.load(in, KEYSTORE_PASSWORD);
+ kmf = KeyManagerFactory.getInstance(KEYMANAGERFACTORY);
+ kmf.init(ks, KEY_PASSWORD);
+ sslcontext = SSLContext.getInstance(SSLCONTEXT_PROTOCOL);
+ sslcontext.init(kmf.getKeyManagers(), null, null);
+ SSLSocketFactory sslFactory = sslcontext.getSocketFactory();
+ hashHost.put(host, sslFactory);
+ log.info("KeyStore for SSL loaded OK and put host in map ("+host+")");
+ return sslFactory;
+ } catch (NoSuchAlgorithmException e) {
+ except=e;
+ } catch (KeyManagementException e) {
+ except=e;
+ } catch (KeyStoreException e) {
+ except=e;
+ } catch (UnrecoverableKeyException e) {
+ except=e;
+ } catch (CertificateException e) {
+ except=e;
+ } finally {
+ if (except != null){
+ log.error("Problem with SSL certificate",except);
+ }
+ IOUtils.closeQuietly(in);
+ }
+ } else {
+ throw new IOException("Unable to read keystore");
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Negotiate a SSL connection.
+ *
+ * @param sock socket in
+ * @param host
+ * @return a new client socket over ssl
+ * @throws Exception if negotiation failed
+ */
+ private Socket startSSL(Socket sock, String host) throws IOException {
+ SSLSocketFactory sslFactory = getSSLSocketFactory(host);
+ SSLSocket secureSocket;
+ if (sslFactory != null) {
+ try {
+ secureSocket = (SSLSocket) sslFactory.createSocket(sock,
+ sock.getInetAddress().getHostName(), sock.getPort(), true);
+ secureSocket.setUseClientMode(false);
+ if (log.isDebugEnabled()){
+ log.debug("SSL transaction ok with cipher: " + secureSocket.getSession().getCipherSuite());
+ }
+ return secureSocket;
+ } catch (IOException e) {
+ log.error("Error in SSL socket negotiation: ", e);
+ throw e;
+ }
+ } else {
+ log.warn("Unable to negotiate SSL transaction, no keystore?");
+ throw new IOException("Unable to negotiate SSL transaction, no keystore?");
+ }
+ }
+
+ /**
+ * Open the local certificate file.
+ *
+ * @return stream to key cert; null if there was a problem opening it
+ */
+ private InputStream getCertificate() {
+ File certFile = new File(CERT_DIRECTORY, CERT_FILE);
+ InputStream in = null;
+ final String certPath = certFile.getAbsolutePath();
+ if (certFile.exists() && certFile.canRead()) {
+ try {
+ in = new FileInputStream(certFile);
+ log.info("Opened Keystore file: "+certPath);
+ } catch (FileNotFoundException e) {
+ log.error("No server cert file found: "+certPath, e);
+ }
+ } else {
+ log.error("No server cert file found: "+certPath);
+ }
+ return in;
+ }
+
+ private SampleResult generateErrorResult(SampleResult result, Exception e) {
+ if (result == null) {
+ result = new SampleResult();
+ result.setSampleLabel("Sample failed");
+ }
+ result.setResponseMessage(e.getMessage());
+ return result;
+ }
+
+ /**
+ * Write output to the output stream, then flush and close the stream.
+ *
+ * @param inBytes
+ * the bytes to write
+ * @param out
+ * the output stream to write to
+ * @param forcedHTTPS if we changed the protocol to https
+ * @throws IOException
+ * if an IOException occurs while writing
+ */
+ private void writeToClient(SampleResult res, OutputStream out, boolean forcedHTTPS) throws IOException {
+ try {
+ String responseHeaders = massageResponseHeaders(res, forcedHTTPS);
+ out.write(responseHeaders.getBytes()); // TODO - charset?
+ out.write(CRLF_BYTES);
+ out.write(res.getResponseData());
+ out.flush();
+ log.debug("Done writing to client");
+ } catch (IOException e) {
+ log.error("", e);
+ throw e;
+ } finally {
+ try {
+ out.close();
+ } catch (Exception ex) {
+ log.warn("Error while closing socket", ex);
+ }
+ }
+ }
+
+ /**
+ * In the event the content was gzipped and unpacked, the content-encoding
+ * header must be removed and the content-length header should be corrected.
+ *
+ * The Transfer-Encoding header is also removed.
+ * If the protocol was changed to HTTPS then change any Location header back to http
+ * @param res - response
+ * @param forcedHTTPS if we changed the protocol to https
+ *
+ * @return updated headers to be sent to client
+ */
+ private String massageResponseHeaders(SampleResult res, boolean forcedHTTPS) {
+ String headers = res.getResponseHeaders();
+ String [] headerLines=headers.split(NEW_LINE, 0); // drop empty trailing content
+ int contentLengthIndex=-1;
+ boolean fixContentLength = forcedHTTPS;
+ for (int i=0;i<headerLines.length;i++){
+ String line=headerLines[i];
+ String[] parts=line.split(":\\s+",2); // $NON-NLS-1$
+ if (parts.length==2){
+ if (HTTPConstants.TRANSFER_ENCODING.equalsIgnoreCase(parts[0])){
+ headerLines[i]=null; // We don't want this passed on to browser
+ continue;
+ }
+ if (HTTPConstants.HEADER_CONTENT_ENCODING.equalsIgnoreCase(parts[0])
+ &&
+ HTTPConstants.ENCODING_GZIP.equalsIgnoreCase(parts[1])
+ ){
+ headerLines[i]=null; // We don't want this passed on to browser
+ fixContentLength = true;
+ continue;
+ }
+ if (HTTPConstants.HEADER_CONTENT_LENGTH.equalsIgnoreCase(parts[0])){
+ contentLengthIndex=i;
+ continue;
+ }
+ final String HTTPS_PREFIX = "https://";
+ if (forcedHTTPS && HTTPConstants.HEADER_LOCATION.equalsIgnoreCase(parts[0])
+ && parts[1].substring(0, HTTPS_PREFIX.length()).equalsIgnoreCase(HTTPS_PREFIX)){
+ headerLines[i]=headerLines[i].replaceFirst(parts[1].substring(0,HTTPS_PREFIX.length()), "http://");
+ continue;
+ }
+ if (forcedHTTPS && HTTPConstants.HEADER_COOKIE.equalsIgnoreCase(parts[0]) || HTTPConstants.HEADER_SET_COOKIE.equalsIgnoreCase(parts[0]))
+ {
+ headerLines[i]=headerLines[i].replaceAll(" secure", "").trim(); //in forced https cookies need to be unsecured...
+ }
+ }
+ }
+ if (fixContentLength && contentLengthIndex>=0){// Fix the content length
+ headerLines[contentLengthIndex]=HTTPConstants.HEADER_CONTENT_LENGTH+": "+res.getResponseData().length;
+ }
+ StringBuilder sb = new StringBuilder(headers.length());
+ for (int i=0;i<headerLines.length;i++){
+ String line=headerLines[i];
+ if (line != null){
+ sb.append(line).append(CRLF_STRING);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Write an error message to the client. The message should be the full HTTP
+ * response.
+ *
+ * @param message
+ * the message to write
+ */
+ private void writeErrorToClient(String message) {
+ try {
+ OutputStream sockOut = clientSocket.getOutputStream();
+ DataOutputStream out = new DataOutputStream(sockOut);
+ out.writeBytes(message);
+ out.flush();
+ } catch (Exception e) {
+ log.warn("Exception while writing error", e);
+ }
+ }
+
+ /**
+ * Add the page encoding of the sample result to the Map with page encodings
+ *
+ * @param result the sample result to check
+ * @return the page encoding found for the sample result, or null
+ */
+ private String addPageEncoding(SampleResult result) {
+ String pageEncoding = ConversionUtils.getEncodingFromContentType(result.getContentType());
+ if(pageEncoding != null) {
+ String urlWithoutQuery = getUrlWithoutQuery(result.getURL());
+ synchronized(pageEncodings) {
+ pageEncodings.put(urlWithoutQuery, pageEncoding);
+ }
+ }
+ return pageEncoding;
+ }
+
+ /**
+ * Add the form encodings for all forms in the sample result
+ *
+ * @param result the sample result to check
+ * @param pageEncoding the encoding used for the sample result page
+ */
+ private void addFormEncodings(SampleResult result, String pageEncoding) {
+ FormCharSetFinder finder = new FormCharSetFinder();
+ if (!result.getContentType().startsWith("text/")){ // TODO perhaps make more specific than this?
+ return; // no point parsing anything else, e.g. GIF ...
+ }
+ try {
+ finder.addFormActionsAndCharSet(result.getResponseDataAsString(), formEncodings, pageEncoding);
+ }
+ catch (HTMLParseException parseException) {
+ log.debug("Unable to parse response, could not find any form character set encodings");
+ }
+ }
+
+ private String getUrlWithoutQuery(URL url) {
+ String fullUrl = url.toString();
+ String urlWithoutQuery = fullUrl;
+ String query = url.getQuery();
+ if(query != null) {
+ // Get rid of the query and the ?
+ urlWithoutQuery = urlWithoutQuery.substring(0, urlWithoutQuery.length() - query.length() - 1);
+ }
+ return urlWithoutQuery;
+ }
+}
View
1,002 src/protocol/amf/org/apache/jmeter/protocol/amf/proxy/AmfProxyControl.java
@@ -0,0 +1,1002 @@
+/*
+* Copyright 2011 the original author or authors.
+*
+* Licensed 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.amf.proxy;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.jmeter.assertions.ResponseAssertion;
+import org.apache.jmeter.assertions.gui.AssertionGui;
+import org.apache.jmeter.config.Arguments;
+import org.apache.jmeter.config.ConfigElement;
+import org.apache.jmeter.config.ConfigTestElement;
+import org.apache.jmeter.control.GenericController;
+import org.apache.jmeter.control.TransactionController;
+import org.apache.jmeter.control.gui.LogicControllerGui;
+import org.apache.jmeter.control.gui.TransactionControllerGui;
+import org.apache.jmeter.engine.util.ValueReplacer;
+import org.apache.jmeter.exceptions.IllegalUserActionException;
+import org.apache.jmeter.functions.InvalidVariableException;
+import org.apache.jmeter.gui.GuiPackage;
+import org.apache.jmeter.gui.tree.JMeterTreeModel;
+import org.apache.jmeter.gui.tree.JMeterTreeNode;
+import org.apache.jmeter.protocol.amf.sampler.AmfRequest;
+import org.apache.jmeter.protocol.amf.util.AmfXmlConverter;
+import org.apache.jmeter.protocol.http.control.HeaderManager;
+import org.apache.jmeter.protocol.http.control.RecordingController;
+import org.apache.jmeter.protocol.http.gui.HeaderPanel;
+import org.apache.jmeter.protocol.http.proxy.Daemon;
+import org.apache.jmeter.protocol.http.proxy.ProxyControl;
+import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase;
+import org.apache.jmeter.protocol.http.sampler.HTTPSamplerFactory;
+import org.apache.jmeter.samplers.SampleEvent;
+import org.apache.jmeter.samplers.SampleListener;
+import org.apache.jmeter.samplers.SampleResult;
+import org.apache.jmeter.testbeans.TestBean;
+import org.apache.jmeter.testelement.TestElement;
+import org.apache.jmeter.testelement.TestListener;
+import org.apache.jmeter.testelement.TestPlan;
+import org.apache.jmeter.testelement.WorkBench;
+import org.apache.jmeter.testelement.property.BooleanProperty;
+import org.apache.jmeter.testelement.property.CollectionProperty;
+import org.apache.jmeter.testelement.property.IntegerProperty;
+import org.apache.jmeter.testelement.property.JMeterProperty;
+import org.apache.jmeter.testelement.property.PropertyIterator;
+import org.apache.jmeter.testelement.property.StringProperty;
+import org.apache.jmeter.threads.AbstractThreadGroup;
+import org.apache.jmeter.timers.Timer;
+import org.apache.jmeter.util.JMeterUtils;
+import org.apache.jorphan.logging.LoggingManager;
+import org.apache.log.Logger;
+import org.apache.oro.text.MalformedCachePatternException;
+import org.apache.oro.text.regex.Pattern;
+import org.apache.oro.text.regex.Perl5Compiler;
+
+
+/**
+ * Class handles storing of generated samples, etc
+ */
+public class AmfProxyControl extends GenericController implements TestBean {
+
+ private static final Logger log = LoggingManager.getLoggerForClass();
+
+ private static final long serialVersionUID = 240L;
+
+ private static final String ASSERTION_GUI = AssertionGui.class.getName();
+
+
+ private static final String TRANSACTION_CONTROLLER_GUI = TransactionControllerGui.class.getName();
+
+ private static final String LOGIC_CONTROLLER_GUI = LogicControllerGui.class.getName();