You are viewing a plain text version of this content. The canonical link for it is here.
Posted to jmeter-dev@jakarta.apache.org by ms...@apache.org on 2001/06/14 02:28:22 UTC
cvs commit: jakarta-jmeter/src/org/apache/jmeter/protocol/http/save HeaderManagerHandler.java
mstover1 01/06/13 17:28:22
Modified: src/org/apache/jmeter/protocol/http/control
HttpTestSample.java
src/org/apache/jmeter/protocol/http/sampler HTTPSampler.java
Added: src/org/apache/jmeter/protocol/http/control Header.java
HeaderManager.java
src/org/apache/jmeter/protocol/http/gui HeaderPanel.java
src/org/apache/jmeter/protocol/http/proxy Admin.java
Cache.java Config.java Daemon.java
HttpReplyHdr.java HttpRequestHdr.java Proxy.java
src/org/apache/jmeter/protocol/http/save
HeaderManagerHandler.java
Log:
New HeaderManager and new proxy service for recording browser actions
Revision Changes Path
1.14 +2 -1 jakarta-jmeter/src/org/apache/jmeter/protocol/http/control/HttpTestSample.java
Index: HttpTestSample.java
===================================================================
RCS file: /home/cvs/jakarta-jmeter/src/org/apache/jmeter/protocol/http/control/HttpTestSample.java,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -r1.13 -r1.14
--- HttpTestSample.java 2001/06/07 23:38:46 1.13
+++ HttpTestSample.java 2001/06/14 00:28:18 1.14
@@ -71,7 +71,7 @@
* Apache Foundation
*
*@author Michael Stover
- *@created $Date: 2001/06/07 23:38:46 $
+ *@created $Date: 2001/06/14 00:28:18 $
*@version 1.0
*/
@@ -237,6 +237,7 @@
static {
addableList.add(new UrlConfig().getClassLabel());
addableList.add(new CookieManager().getClassLabel());
+ addableList.add(new HeaderManager().getClassLabel());
addableList.add(new AuthManager().getClassLabel());
}
1.1 jakarta-jmeter/src/org/apache/jmeter/protocol/http/control/Header.java
Index: Header.java
===================================================================
/*
* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache JMeter" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache JMeter", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.jmeter.protocol.http.control;
import org.apache.jmeter.config.*;
/**
* This class is an HTTP Header encapsulator.
*
* @author <a href="mailto:giacomo@apache.org">Giacomo Pati</a>
*/
public class Header extends AbstractConfigElement {
private static String NAME = "name";
private static String VALUE = "value";
/**
* create the headeer
*/
public Header() {
this.setName("");
this.setValue("");
}
/**
* create the coookie
*/
public Header(String name, String value) {
this.setName(name);
this.setValue(value);
}
public void addConfigElement(ConfigElement config){
}
public boolean expectsModification() {
return false;
}
public String getClassLabel() {
return "Header";
}
/**
* get the value for this object.
*/
public String getValue() {
return (String)this.getProperty(VALUE);
}
public Object clone() {
Header newConfig = new Header();
configureClone(newConfig);
return newConfig;
}
/**
* set the value for this object.
*/
public synchronized void setValue(String value) {
this.putProperty(VALUE,value);
}
/**
* creates a string representation of this header
*/
public String toString() {
return getName() + "\t" + getValue();
}
}
1.1 jakarta-jmeter/src/org/apache/jmeter/protocol/http/control/HeaderManager.java
Index: HeaderManager.java
===================================================================
/*
* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache JMeter" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache JMeter", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.jmeter.protocol.http.control;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import javax.swing.table.AbstractTableModel;
import org.apache.jmeter.config.*;
import org.apache.jmeter.gui.JMeterComponentModel;
import org.apache.jmeter.protocol.http.save.*;
import org.apache.jmeter.save.Saveable;
/**
* This class provides an interface to headers file to
* pass HTTP headers along with a request.
*
* @author <a href="mailto:giacomo@apache.org">Giacomo Pati</a>
* @version $Revision: 1.1 $ $Date: 2001/06/14 00:28:18 $
*/
public class HeaderManager extends AbstractTableModel implements ConfigElement,JMeterComponentModel,Saveable {
public static final String HEADERS = "headers";
/**
* A vector of Headers managed by this class.
* @associates <{org.apache.jmeter.controllers.Header}>
*/
private List headers = new ArrayList();
private String name;
private final static int columnCount = 2;
private final static String[] columnNames = {"Name","Value"};
private static List addableList = new LinkedList();
public HeaderManager () {
}
public Class getTagHandlerClass() {
return org.apache.jmeter.protocol.http.save.HeaderManagerHandler.class;
}
public void uncompile() {
}
public Object clone() {
HeaderManager ck = new HeaderManager();
for (int x = 0; x < headers.size(); x++) {
ck.add((Header)headers.get(x));
}
ck.setName(this.name);
return ck;
}
public boolean expectsModification() {
return false;
}
public Collection getAddList() {
return addableList;
}
public boolean isEditable() {
return true;
}
public Class getGuiClass() {
return org.apache.jmeter.protocol.http.gui.HeaderPanel.class;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
/************************************************************
* This allows config elements to combine and give a "layered" effect. for
* example, say there are two HTTPConfigElements, which have properties for
* domain, path, method, and parameters. If element A has everything filled
* in, but null for domain, and element B is added, which has only domain
* filled in, then after adding B to A, A will have the domain from B. If A
* already had a domain, then the correct behavior is for A to ignore the
* addition of element B.
*
*@param config !ToDo
***********************************************************/
public void addConfigElement(ConfigElement config) {
if (config instanceof HeaderManager) {
this.headers.addAll(((HeaderManager)config).getHeaders());
}
}
public Collection getHeaders() {
return headers;
}
/** required by table model interface */
public int getRowCount() {
return headers.size();
}
/** required by table model interface */
public int getColumnCount() {
return columnCount;
}
/** required by table model interface */
public String getColumnName(int column) {
return columnNames[column];
}
/** required by table model interface */
public Class getColumnClass(int column) {
return columnNames[column].getClass();
}
/** required by table model interface */
public Object getValueAt(int row, int column) {
Header head = (Header) headers.get(row);
if (column == 0) {
return head.getName();
} else if (column == 1) {
return head.getValue();
}
return null;
}
/** save the header data to a file */
public void save(String headFile) throws IOException {
File file = new File(headFile);
if (!file.isAbsolute()) {
file = new File(System.getProperty("user.dir") + File.separator + headFile);
}
PrintWriter writer = new PrintWriter(new FileWriter(file));
writer.println("# JMeter generated Header file");
for (int i = 0; i < headers.size(); i++) {
Header head = (Header) headers.get(i);
writer.println(head.toString());
}
writer.flush();
writer.close();
}
/** add header data from a file */
public void addFile (String headerFile) throws IOException {
File file = new File(headerFile);
if (!file.isAbsolute()) {
file = new File(System.getProperty("user.dir") + File.separator + headerFile);
}
BufferedReader reader = null;
if (file.canRead()) {
reader = new BufferedReader(new FileReader(file));
} else {
throw new IOException("The file you specified cannot be read.");
}
String line;
while ((line = reader.readLine()) != null) {
try {
if (line.startsWith("#") || line.trim().length() == 0) {
continue;
}
String[] st = split(line, "\t"," ");
int name = 0;
int value = 1;
Header header = new Header(st[name], st[value]);
headers.add(header);
} catch (Exception e) {
throw new IOException("Error parsing header line\n\t'" + line + "'\n\t" + e);
}
}
reader.close();
}
/** add a header */
public void add(Header h) {
headers.add(h);
}
/** remove a header */
public void remove(int index) {
headers.remove(index);
}
/** return the number headers */
public int size() {
return headers.size();
}
/** return the header at index i */
public Header get(int i) {
return (Header) headers.get(i);
}
/*
public String getHeaderHeaderForURL(URL url) {
if (!url.getProtocol().toUpperCase().trim().equals("HTTP") &&
! url.getProtocol().toUpperCase().trim().equals("HTTPS")) {
return null;
}
StringBuffer sbHeader = new StringBuffer();
for (Iterator enum = headers.iterator(); enum.hasNext();) {
Header header = (Header) enum.next();
if (url.getHost().endsWith(header.getDomain()) &&
url.getFile().startsWith(header.getPath()) &&
(System.currentTimeMillis() / 1000) <= header.getExpires()) {
if (sbHeader.length() > 0) {
sbHeader.append("; ");
}
sbHeader.append(header.getName()).append("=").append(header.getValue());
}
}
if (sbHeader.length() != 0) {
return sbHeader.toString();
} else {
return null;
}
}
*/
/*
public void addHeaderFromHeader(String headerHeader, URL url) {
StringTokenizer st = new StringTokenizer(headerHeader, ";");
String nvp;
// first n=v is name=value
nvp = st.nextToken();
int index = nvp.indexOf("=");
String name = nvp.substring(0,index);
String value = nvp.substring(index+1);
String domain = url.getHost();
Header newHeader = new Header(name, value);
// check the rest of the headers
while (st.hasMoreTokens()) {
nvp = st.nextToken();
nvp = nvp.trim();
index = nvp.indexOf("=");
if(index == -1) {
index = nvp.length();
}
String key = nvp.substring(0,index);
Vector removeIndices = new Vector();
for (int i = headers.size() - 1; i >= 0; i--) {
Header header = (Header) headers.get(i);
if (header == null) {
continue;
}
if (header.getName().equals(newHeader.getName())) {
removeIndices.addElement(new Integer(i));
}
}
for (Enumeration e = removeIndices.elements(); e.hasMoreElements();) {
index = ((Integer) e.nextElement()).intValue();
headers.remove(index);
}
}
*/
public void removeHeaderNamed(String name) {
Vector removeIndices = new Vector();
for (int i = headers.size() - 1; i > 0; i--) {
Header header = (Header) headers.get(i);
if (header == null) {
continue;
}
if (header.getName().equals(name)) {
removeIndices.addElement(new Integer(i));
}
}
for (Enumeration e = removeIndices.elements(); e.hasMoreElements();) {
headers.remove(((Integer) e.nextElement()).intValue());
}
}
/******************************************************
* Takes a String and a tokenizer character, and returns
a new array of strings of the string split by the tokenizer
character.
@param splittee String to be split
@param splitChar Character to split the string on
@param def Default value to place between two split chars that have
nothing between them
@return Array of all the tokens.
******************************************************/
public String[] split(String splittee, String splitChar, String def) {
if(splittee == null || splitChar == null) {
return new String[0];
}
StringTokenizer tokens;
String temp;
int spot;
while((spot=splittee.indexOf(splitChar + splitChar))!=-1) {
splittee=splittee.substring(0, spot + splitChar.length()) + def +
splittee.substring(spot + (1*splitChar.length()), splittee.length());
}
Vector returns=new Vector();
tokens = new StringTokenizer(splittee, splitChar);
while(tokens.hasMoreTokens()) {
temp = (String)tokens.nextToken();
returns.addElement(temp);
}
String[] values=new String[returns.size()];
returns.copyInto(values);
return values;
}
public String getClassLabel() {
return "Header Manager";
}
}
1.1 jakarta-jmeter/src/org/apache/jmeter/protocol/http/gui/HeaderPanel.java
Index: HeaderPanel.java
===================================================================
/*
* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache JMeter" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache JMeter", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.jmeter.protocol.http.gui;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import org.apache.jmeter.protocol.http.control.*;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jmeter.gui.*;
/************************************************************
* Allows the user to specify if she needs HTTP header services,
* and give parameters
* for this service.
*
*@author $Author: mstover1 $
*@created $Date: 2001/06/14 00:28:19 $
*@version $Revision: 1.1 $
***********************************************************/
public class HeaderPanel extends JPanel implements ModelSupported {
HeaderManager manager;
/************************************************************
* A table to show the authentication information
***********************************************************/
JTable headerTable;
NamePanel namePanel;
/************************************************************
* Default constructor
***********************************************************/
public HeaderPanel()
{
}
/************************************************************
* !ToDo (Method description)
*
*@param model !ToDo (Parameter description)
***********************************************************/
public void setModel(Object model)
{
manager = (HeaderManager)model;
init();
}
public void updateGui()
{
manager.fireTableDataChanged();
namePanel.updateGui();
}
/************************************************************
* Gets a HeaderManager to manage the file that is currently selected. Null is
* returned if no file is currently selected. Null will also be returned if
* there is a problem reading the file while getting the HeaderManager.
*
*@return !ToDo (Return description)
*@returns A HeaderManager for the current file, or null
***********************************************************/
public HeaderManager getHeaderMgr()
{
return manager;
}
/************************************************************
* Shows the main header configuration panel
***********************************************************/
public void init()
{
this.setLayout(new VerticalLayout());
namePanel = new NamePanel(manager);
this.add(namePanel);
headerTable = new JTable(manager);
headerTable.setCellSelectionEnabled(false);
headerTable.setRowSelectionAllowed(true);
headerTable.setColumnSelectionAllowed(false);
headerTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
JPanel main = new JPanel();
main.setLayout(new BorderLayout());
JScrollPane scroller = new JScrollPane(headerTable);
JTableHeader tableHeader = headerTable.getTableHeader();
scroller.setColumnHeaderView(tableHeader);
main.add(scroller);
JButton add = new JButton("Add");
JButton edit = new JButton("Edit");
JButton delete = new JButton("Delete");
//JButton close = new JButton("Close");
JButton load = new JButton("Load");
JButton save = new JButton("Save");
Dimension d = delete.getPreferredSize();
add.setPreferredSize(d);
edit.setPreferredSize(d);
//close.setPreferredSize(d);
load.setPreferredSize(d);
save.setPreferredSize(d);
HeaderUpdater updater = new HeaderUpdater();
add.addActionListener(updater);
edit.addActionListener(updater);
delete.addActionListener(
new ActionListener()
{
/************************************************************
* !ToDo (Method description)
*
*@param e !ToDo (Parameter description)
***********************************************************/
public void actionPerformed(ActionEvent e)
{
int row = headerTable.getSelectedRow();
if (row >= 0)
{
manager.remove(row);
}
manager.fireTableDataChanged();
}
});
load.addActionListener(
new ActionListener()
{
/************************************************************
* !ToDo (Method description)
*
*@param e !ToDo (Parameter description)
***********************************************************/
public void actionPerformed(ActionEvent e)
{
try
{
File tmp = FileDialoger.promptToOpenFile()
.getSelectedFile();
if (tmp != null)
{
manager.addFile(tmp.getAbsolutePath());
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
});
save.addActionListener(
new ActionListener()
{
/************************************************************
* !ToDo (Method description)
*
*@param e !ToDo (Parameter description)
***********************************************************/
public void actionPerformed(ActionEvent e)
{
try
{
File tmp = FileDialoger.promptToOpenFile()
.getSelectedFile();
if (tmp != null)
{
manager.save(tmp.getAbsolutePath());
}
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
});
JPanel panel = new JPanel();
GridBagLayout g = new GridBagLayout();
panel.setLayout(g);
GridBagConstraints c = new GridBagConstraints();
c.fill = c.NONE;
c.gridwidth = 1;
c.gridheight = 1;
c.gridx = 1;
c.gridy = 1;
g.setConstraints(add, c);
panel.add(add);
c.gridx = 2;
c.gridy = 1;
g.setConstraints(edit, c);
panel.add(edit);
c.gridx = 3;
c.gridy = 1;
g.setConstraints(delete, c);
panel.add(delete);
/*
* c.gridx = 1;
* c.gridy = 2;
* g.setConstraints(close, c);
* panel.add(close);
*/
c.gridx = 2;
c.gridy = 2;
g.setConstraints(load, c);
panel.add(load);
c.gridx = 3;
c.gridy = 2;
g.setConstraints(save, c);
panel.add(save);
main.add(panel, "South");
this.add(main);
}
/************************************************************
* Updates a header record
*
*@author $Author: mstover1 $
*@created $Date: 2001/06/14 00:28:19 $
*@version $Revision: 1.1 $
***********************************************************/
class HeaderUpdater implements ActionListener
{
int index;
JTextField nameField = new JTextField(20);
JTextField valueField = new JTextField(20);
JButton ok = new JButton("Ok");
JButton cancel = new JButton("Cancel");
JDialog updateDialog;
/************************************************************
* !ToDo (Constructor description)
***********************************************************/
public HeaderUpdater()
{
}
/************************************************************
* returns the contructed panel containing the header record
*
*@return !ToDo (Return description)
***********************************************************/
public JPanel getPanel()
{
JPanel main = new JPanel();
GridBagLayout g = new GridBagLayout();
main.setLayout(g);
GridBagConstraints c = new GridBagConstraints();
c.fill = c.BOTH;
c.gridwidth = 1;
c.gridheight = 1;
JLabel nameLabel = new JLabel("Name:");
c.gridx = 1;
c.gridy = 1;
g.setConstraints(nameLabel, c);
main.add(nameLabel);
JLabel valueLabel = new JLabel("Value:");
c.gridx = 1;
c.gridy = 2;
g.setConstraints(valueLabel, c);
main.add(valueLabel);
c.gridx = 2;
c.gridy = 1;
g.setConstraints(nameField, c);
main.add(nameField);
c.gridx = 2;
c.gridy = 2;
g.setConstraints(valueField, c);
main.add(valueField);
JPanel buttons = new JPanel();
ok.setPreferredSize(cancel.getPreferredSize());
buttons.add(ok);
buttons.add(cancel);
c.gridwidth = 2;
c.gridx = 1;
c.gridy = 7;
g.setConstraints(buttons, c);
main.add(buttons);
return main;
}
/************************************************************
* !ToDo (Method description)
*
*@param e !ToDo (Parameter description)
***********************************************************/
public void actionPerformed(ActionEvent e)
{
String command = e.getActionCommand();
boolean valid = true;
index = -1;
if (command.equals("Edit"))
{
index = headerTable.getSelectedRow();
if (index < 0)
{
valid = false;
}
else
{
Header c = manager.get(index);
nameField = new JTextField(c.getName(), 20);
valueField = new JTextField(c.getValue(), 20);
ok = new JButton("Ok");
cancel = new JButton("Cancel");
}
}
else if (command.equals("Add"))
{
nameField = new JTextField(20);
valueField = new JTextField(20);
ok = new JButton("Ok");
cancel = new JButton("Cancel");
}
if (valid)
{
if (updateDialog != null)
{
updateDialog.dispose();
}
updateDialog = new JDialog();
updateDialog.setSize(350, 300);
ok.addActionListener(
new ActionListener()
{
/************************************************************
* !ToDo (Method description)
*
*@param ev !ToDo (Parameter description)
***********************************************************/
public void actionPerformed(ActionEvent ev)
{
int i = index;
Header c = new Header();
if (i >= 0)
{
c = manager.get(index);
}
c.setName(nameField.getText());
c.setValue(valueField.getText());
if (i < 0)
{
manager.add(c);
}
manager.fireTableDataChanged();
updateDialog.dispose();
}
});
cancel.addActionListener(
new ActionListener()
{
/************************************************************
* !ToDo (Method description)
*
*@param ev !ToDo (Parameter description)
***********************************************************/
public void actionPerformed(ActionEvent ev)
{
updateDialog.dispose();
}
});
updateDialog.getContentPane().add(getPanel());
updateDialog.show();
}
}
}
}
1.1 jakarta-jmeter/src/org/apache/jmeter/protocol/http/proxy/Admin.java
Index: Admin.java
===================================================================
package org.apache.jmeter.protocol.http.proxy;
/******************************************************************
*** File Admin.java
***
***/
import java.net.*;
import java.io.*;
//
// Class: Admin
// Abstract: The admin thread listens on admin socket and handle all
// communications with the remote administrator.
//
public class Admin extends Thread
{
//
// Member variables
//
ServerSocket adminSocket = null;
Socket appletSocket = null;
String passwordCandidate = null;
DataInputStream in = null;
DataOutputStream out = null;
Config config = null;
Cache cache;
//
// Public methods
//
//
// Constructor
//
Admin(Config configObject, Cache cacheManager)
{
try
{
config = configObject;
cache = cacheManager;
adminSocket = new ServerSocket(0);
config.setAdminPort(adminSocket.getLocalPort());
}
catch (IOException e)
{
System.out.println("Error opening admin socket");
}
}
//
// Handle communications with remote administrator
//
public void run()
{
while(true)
{
try
{
appletSocket = adminSocket.accept();
in = new DataInputStream(appletSocket.getInputStream());
out = new DataOutputStream(appletSocket.getOutputStream());
do
{
// Read password candidate sent by applet
String passwordCandidate = in.readLine();
// Send applet ack/nack on password
if (config.getPassword().equals(passwordCandidate))
{
out.writeBytes("ACCEPT\n");
break;
}
else
{
out.writeBytes("REJECT\n");
}
out.flush();
}
while (true);
//
// Password is OK, so let's send the administrator the
// parameters values and read his new values
//
while(true)
{
out.writeBytes(config.toString());
out.flush();
config.parse(in.readLine());
System.out.println("Configuration changed by administrator.");
// Administrator wants to clean the cache
if (config.getCleanCache())
{
cache.clean();
config.setCleanCache(false); //no need to clean again
}
}
}
catch (Exception e)
{
//
// This line was reached because the administrator closed
// the connection with the proxy. That's fine, we are now
// available for another administrator to log in.
//
System.out.println("Connection with administrator closed.");
}
finally
{
try
{
out.close();
in.close();
}
catch(Exception exc)
{}
}
}//while
}
}
1.1 jakarta-jmeter/src/org/apache/jmeter/protocol/http/proxy/Cache.java
Index: Cache.java
===================================================================
package org.apache.jmeter.protocol.http.proxy;
/******************************************************************
*** File Cache.java
***
***/
import java.io.*;
import java.util.*;
import java.net.*;
//
// Class: Cache
// Abstract: manages all caching activities.
//
public class Cache
{
//
// Members variables
//
String basePath = null;
long MinFreeSpace;// in bytes
Hashtable htable;
Config config;
//
// Public methods
//
//
// Constructor
//
public Cache(Config configObject)
{
//
// Initialize variables
//
config = configObject;
MinFreeSpace = 15000;
htable = new Hashtable();
//
// Create directory for caching
//
File cacheDir = new File("Cache");
cacheDir.mkdirs();
basePath = cacheDir.getAbsolutePath();
//
// Delete all files in cache directory
//
int i;
File file = new File(basePath);;
String filename;
// Get list of files in cache direcotry
String files[] = file.list();
// Delete each file found
for (i=0; i<files.length; i++)
{
file = new File(basePath + File.separatorChar + files[i]);
file.delete();
}
config.setFilesCached(0);
config.setBytesCached(0);
config.setHits(0);
config.setMisses(0);
}
//
// isCachable - check if URL reply should be cached
//
public boolean IsCachable(String rawUrl)
{
return (getFileName(rawUrl) != null);
}
//
// IsCached - Check if we have in cache what the client wants.
//
public boolean IsCached(String rawUrl)
{
// Generate filename from URL
String filename = getFileName(rawUrl);
if (filename == null)
return false;
// Search in hash table
if (htable.get(filename) != null)
return true;
return false;
}
//
// getFileInputStream - When this method is called, it means a cache hit.
// We update the date field in the hash table entry and return a
// FileInputStream object corresponding to the file caching the info.
//
public FileInputStream getFileInputStream(String rawUrl)
{
FileInputStream in = null;
try
{
String filename = getFileName(rawUrl);
// Update the hash table entry with current date as value
htable.put(filename,new Date());
in = new FileInputStream(filename);
}
catch (FileNotFoundException fnf)
{
try
{
System.out.println("File Not Found:"+getFileName(rawUrl)+" "+fnf);
}
catch (Exception e)
{}
}
finally
{
return in;
}
}
//
// getFileoutputStream - When this method is called, it means we're about
// to cache a new object. We generate a file name, and return
// a corresponding FileOutputStream object.
//
public FileOutputStream getFileOutputStream(String rawUrl)
{
FileOutputStream out = null;
String filename;
try
{
filename = getFileName(rawUrl);
out = new FileOutputStream(filename);
}
catch (IOException e)
{}
finally
{
return out;
}
}
//
// Decrement Cache Free Space (In Bytes)
//
public synchronized void DecrementFreeSpace(int nbytes, String rawUrl)
{
config.setBytesCached(config.getBytesCached() + nbytes);
if (config.getBytesFree() <= MinFreeSpace)
MakeFreeSpace(rawUrl);
}
//
// Add new entry to hash table
//
public synchronized void AddToTable(String rawUrl)
{
String filename = getFileName(rawUrl);
// Add filename to hash table with the current date as its value
htable.put(filename,new Date());
config.increaseFilesCached();
}
//
// clean - delete the cached files
//
public synchronized void clean()
{
System.out.println("Cleaning the cache...");
// Enumerate the hash table
for (Enumeration keys = htable.keys(); keys.hasMoreElements() ;)
{
String filename = (String)keys.nextElement();
File file = new File(filename);
long nbytes = file.length();
boolean result = file.delete();
if (result == true)
{
// Delete entry in hash table
htable.remove(filename);
config.decreaseFilesCached();
// Increment free space
config.setBytesCached(config.getBytesCached() - nbytes);
}
else
{
// Another thread holds this file open for writing
}
}
config.setHits(0);
config.setMisses(0);
System.out.println("Cache is clean.");
}
//
// Private methods
//
//
// MakeFreeSpace - throw LRU file until free space is above min level
//
private synchronized void MakeFreeSpace(String rawUrl)
{
String filename,
LRUfilename;
Date date,
minDate;
minDate = new Date();
while (config.getBytesFree() < MinFreeSpace)
{
filename = LRUfilename = null;
date = null;
if (htable.isEmpty())
{
System.out.println("Could not make free space: Hash table empty...");
return;
}
//
// Enumerate the hash table entries to find the LRU file
//
for (Enumeration keys = htable.keys(); keys.hasMoreElements() ;)
{
filename = (String)keys.nextElement();
date = (Date)htable.get(filename);
if (date.before(minDate))
LRUfilename = filename;
}
//
// Delete the LRU file
//
File LRUfile = new File(LRUfilename);
long nbytes = LRUfile.length();
boolean result = LRUfile.delete();
if (result == true)
{
// Delete entry in hash table
htable.remove(LRUfilename);
config.decreaseFilesCached();
// Increment free space
config.setBytesCached(config.getBytesCached() - nbytes);
}
else
{
// Another thread holds this file open for writing
System.out.println("File "+LRUfilename+" could not be deleted...");
return;
}
}
}
//
// Convert the URL to filename - this method parses the URL and
// generate filename only if the URL is to be cached.
// We do not cache URLs containing '?', "cgi-bin" and
// a list of not-to-cached-URLs as instructed by the proxy administrator.
//
private String getFileName(String rawUrl)
{
String filename = basePath + File.separatorChar + rawUrl.substring(7).replace('/','@');
if (filename.indexOf('?') != -1 || filename.indexOf("cgi-bin") != -1)
{
return null;
}
return filename;
}
}
1.1 jakarta-jmeter/src/org/apache/jmeter/protocol/http/proxy/Config.java
Index: Config.java
===================================================================
package org.apache.jmeter.protocol.http.proxy;
/******************************************************************
*** File Config.java
***
***/
import java.util.*;
import java.net.*;
import java.io.*;
//
// Class: Config
// Abstract: Configurable parameters of the proxy. This class is
// used by both the applet and the proxy.
//
class Config
{
//
// Member variables
//
String jmxScriptDir;
private boolean isFatherProxy;
private String fatherProxyHost;
private int fatherProxyPort;
private String[] deniedHosts;
private String password;
private boolean isCaching; // enable/disable caching
private long cacheSize; // cache size in bytes
private boolean cleanCache;
private String[] cacheMasks;
private long filesCached;
private long bytesCached;
private long bytesFree;
private long hits;
private long misses;
private final int defaultProxyPort = 8080;
private final String defaultPassword = "admin";
private final long defaultCacheSize = 1000000;
private String adminPath;
private int adminPort;
private String localHost;
private String localIP;
private boolean isAppletContext;
private String separator = " ";
private String proxyMachineNameAndPort;
//
// Member methods
//
//
// Constructor
//
Config()
{
filesCached = 0;
bytesCached = 0;
bytesFree = cacheSize;
hits = 0;
misses = 0;
reset();
}
public void setJmxScriptDir(String filename)
{
jmxScriptDir = filename;
File file = new File(System.getProperty("user.dir")+File.separator+jmxScriptDir);
file.mkdirs();
}
public String getJmxScriptDir()
{
return jmxScriptDir;
}
//
// Re-initialize
//
public void reset()
{
isFatherProxy = false;
fatherProxyHost = "wwwproxy.ac.il";
fatherProxyPort = defaultProxyPort;
password = defaultPassword;
isCaching = true;
cacheSize = defaultCacheSize;
cleanCache = false;
deniedHosts = new String[0];
cacheMasks = new String[0];
}
//
// Set/get methods
//
// Set if we are in the applet or in the proxy
public void setIsAppletContext(boolean b)
{
isAppletContext = b;
}
public void setProxyMachineNameAndPort(String s)
{
proxyMachineNameAndPort = s;
}
public String getProxyMachineNameAndPort()
{
return proxyMachineNameAndPort;
}
public int getAdminPort()
{
return adminPort;
}
public void setAdminPort(int port)
{
adminPort = port;
}
public void setAdminPath(String path)
{
adminPath = path;
}
public String getAdminPath()
{
return adminPath;
}
public void setLocalHost(String host)
{
localHost = host;
}
public String getLocalHost()
{
return localHost;
}
public void setLocalIP(String ip)
{
localIP = ip;
}
public String getLocalIP()
{
return localIP;
}
boolean getIsCaching()
{
return isCaching;
}
public synchronized void setIsCaching(boolean caching)
{
isCaching = caching;
}
public synchronized long getCacheSize()
{
return cacheSize;
}
public synchronized void setCacheSize(long size)
{
cacheSize = size;
}
public boolean getIsFatherProxy()
{
return isFatherProxy;
}
public synchronized void setIsFatherProxy(boolean fatherProxy)
{
isFatherProxy = fatherProxy;
}
public String getFatherProxyHost()
{
return fatherProxyHost;
}
public synchronized void setFatherProxyHost(String host)
{
fatherProxyHost = host;
}
public int getFatherProxyPort()
{
return fatherProxyPort;
}
public synchronized void setFatherProxyPort(int port)
{
fatherProxyPort = port;
}
public String[] getDeniedHosts()
{
return deniedHosts;
}
public synchronized void setDeniedHosts(String[] hosts)
{
deniedHosts = hosts;
}
public String getPassword()
{
return password;
}
public synchronized void setPassword(String newPassword)
{
password = newPassword;
}
public boolean getCleanCache()
{
return cleanCache;
}
public synchronized void setCleanCache(boolean clean)
{
cleanCache = clean;
}
public String[] getCacheMasks()
{
return cacheMasks;
}
public synchronized void setCacheMasks(String[] masks)
{
cacheMasks = masks;
}
public long getFilesCached()
{
return filesCached;
}
public synchronized void increaseFilesCached()
{
filesCached++;
}
public synchronized void decreaseFilesCached()
{
filesCached--;
}
public synchronized void setFilesCached(long number)
{
filesCached = number;
}
public long getBytesCached()
{
return bytesCached;
}
public synchronized void setBytesCached(long number)
{
bytesCached = number;
}
public long getBytesFree()
{
return cacheSize - bytesCached;
}
public long getHits()
{
return hits;
}
public synchronized void increaseHits()
{
hits++;
}
public synchronized void setHits(long number)
{
hits = number;
}
public long getMisses()
{
return misses;
}
public synchronized void increaseMisses()
{
misses++;
}
public synchronized void setMisses(long number)
{
misses = number;
}
public double getHitRatio()
{
if ((hits + misses)==0)
return 0;
else
return 100*hits / (hits + misses);
}
//
// Construct a string with all parameters
//
public synchronized String toString()
{
int i;
String s = "";
s += isFatherProxy + separator;
s += fatherProxyHost.equals("") ? "NULL" : fatherProxyHost;
s += separator +
fatherProxyPort + separator;
s += deniedHosts.length + separator;
for (i=0; i<deniedHosts.length; i++)
s += deniedHosts[i] + separator;
s +=
password + separator +
isCaching + separator +
cacheSize + separator +
cleanCache + separator;
s += cacheMasks.length + separator;
for (i=0; i<cacheMasks.length; i++)
s += cacheMasks[i] + separator;
s+= proxyMachineNameAndPort + separator;
s +=
filesCached + separator +
bytesCached + separator +
bytesFree + separator +
hits + separator +
misses + separator +
"\n";
return s;
}
//
// Set parameters according to a string (that was sent by applet)
//
public synchronized void parse(String config)
{
System.out.println("Parsing administrator request...");
int size,i;
StringTokenizer s = new StringTokenizer(config,separator);
isFatherProxy = s.nextToken().equals("true");
System.out.println("Use father proxy = "+isFatherProxy);
fatherProxyHost = s.nextToken();
if(fatherProxyHost.equals("NULL"))
fatherProxyHost = "";
System.out.println("Father proxy name = "+fatherProxyHost);
fatherProxyPort = Integer.parseInt(s.nextToken());
System.out.println("Father proxy port = "+fatherProxyPort);
size = Integer.parseInt(s.nextToken());
deniedHosts = new String[size];
for (i=0; i<size; i++)
{
deniedHosts[i] = s.nextToken();
System.out.println("Deny access to "+deniedHosts[i]);
}
password = s.nextToken();
System.out.println("password = "+password);
isCaching = s.nextToken().equals("true");
System.out.println("Caching = "+isCaching);
cacheSize = Long.parseLong(s.nextToken());
System.out.println("Cache size = "+cacheSize);
cleanCache = s.nextToken().equals("true");
System.out.println("Do cache clean up = "+cleanCache);
size = Integer.parseInt(s.nextToken());
cacheMasks = new String[size];
for (i=0; i<size; i++)
{
cacheMasks[i] = s.nextToken();
System.out.println("Don't cache "+cacheMasks[i]);
}
proxyMachineNameAndPort = s.nextToken();
if (isAppletContext)
{
filesCached = Long.parseLong(s.nextToken());
bytesCached = Long.parseLong(s.nextToken());
bytesFree = Long.parseLong(s.nextToken());
hits = Long.parseLong(s.nextToken());
misses = Long.parseLong(s.nextToken());
}
//
// Update bytesFree to reflect the change in cache size.
// Note that free bytes can be below min free level now.
//
bytesFree = cacheSize - bytesCached;
}
}
1.1 jakarta-jmeter/src/org/apache/jmeter/protocol/http/proxy/Daemon.java
Index: Daemon.java
===================================================================
package org.apache.jmeter.protocol.http.proxy;
/******************************************************************
*** File Daemon.java
***
***/
import java.net.*;
import java.io.*;
//
// Class: Daemon
// Abstract: Web daemon thread. creates main socket on port 8080
// and listens on it forever. For each client request,
// creates proxy thread to handle the request.
//
public class Daemon extends Thread
{
//
// Member variables
//
static ServerSocket MainSocket = null;
static Cache cache = null;
static Config config;
static String adminPath;
final static int defaultDaemonPort = 8080;
final static int maxDaemonPort = 65536;
//
// Member methods
//
// Application starts here
public static void main(String args[])
{
int daemonPort;
// Parse command line
switch (args.length)
{
case 0: daemonPort = defaultDaemonPort;
break;
case 1: try
{
daemonPort = Integer.parseInt(args[0]);
}
catch (NumberFormatException e)
{
System.out.println("Error: Invalid daemon port");
return;
}
if (daemonPort > maxDaemonPort)
{
System.out.println("Error: Invalid daemon port");
return;
}
break;
default:System.out.println("Usage: Proxy [daemon port]");
return;
}
try
{
// Create the Cache Manager and Configuration objects
System.out.println("Initializing...");
System.out.print("Creating Config Object...");
config = new Config();
config.setIsAppletContext(false);
config.setLocalHost(InetAddress.getLocalHost().getHostName());
String tmp = InetAddress.getLocalHost().toString();
config.setLocalIP(tmp.substring(tmp.indexOf('/')+1));
config.setProxyMachineNameAndPort(InetAddress.getLocalHost().getHostName()+":"+daemonPort);
config.setJmxScriptDir("proxy_script");
File adminDir = new File("Applet");
config.setAdminPath(adminDir.getAbsolutePath());
System.out.println("OK");
System.out.print("Creating Cache Manager...");
cache = new Cache(config);
System.out.println("OK");
// Start the admin thread
System.out.print("Creating Admin Thread...");
Admin adminThd = new Admin(config,cache);
adminThd.start();
System.out.println(" port " + config.getAdminPort() + " OK");
// Create main socket
System.out.print("Creating Daemon Socket...");
MainSocket = new ServerSocket(daemonPort);
System.out.println(" port " + daemonPort + " OK");
if (config.getIsFatherProxy())
{
System.out.println("Using Father Proxy "+
config.getFatherProxyHost()+
":"+config.getFatherProxyPort()+" .");
}
else
{
System.out.println("Not Using Father Proxy .");
}
System.out.println("Proxy up and running!");
// Main loop
while (true)
{
// Listen on main socket
Socket ClientSocket = MainSocket.accept();
// Pass request to new proxy thread
Proxy thd = new Proxy(ClientSocket,cache,config);
thd.start();
}
}
catch (Exception e)
{}
finally
{
try
{
MainSocket.close();
}
catch (Exception exc)
{
}
}
}
}
1.1 jakarta-jmeter/src/org/apache/jmeter/protocol/http/proxy/HttpReplyHdr.java
Index: HttpReplyHdr.java
===================================================================
package org.apache.jmeter.protocol.http.proxy;
/******************************************************************
*** File HttpReplyHdr.java
***
***/
import java.net.*;
import java.io.*;
//
// Class: HttpReplyHdr
// Abstract: The headers of the server HTTP reply.
//
public class HttpReplyHdr
{
static String CR="\r\n";
static String HTTP_PROTOCOL="HTTP/1.0";
static String HTTP_SERVER="Java Proxy Server";
String lastModified ="";
long contentLength=0;
String extraErrorString ="";
/**
* Sets the last modified date for a header;
*
* @param date A string holding an interner date
* @return true
*/
public boolean setModifiedDate(String date)
{
lastModified = date;
return true;
}
/**
* Adds an extra explanation. This extra information is
* Added to the http headers failure explanation.
*
* @param str Description to add.
* @return true.
*/
public boolean addErrorDescription(String str)
{
extraErrorString = str;
return true;
}
/**
* Forms a http ok reply header
*
* @param ContentType The mime-type of the content
* @return A string with the header in it
*/
public String formOk(String ContentType,long ContentLength)
{
contentLength = ContentLength;
String out =new String();
out += HTTP_PROTOCOL + " 200 Ok" + CR;
out += "Server: " + HTTP_SERVER + CR;
out += "MIME-version: 1.0" + CR;
if (0 < ContentType.length())
out += "Content-type: " + ContentType + CR;
else
out += "Content-Type: text/html" + CR;
if (0 != contentLength)
out += "Content-Length: " + Long.toString(contentLength) + CR;
if (0 < lastModified.length())
out +="Last-Modified: " + lastModified + CR;
out +=CR;
return out;
}
/**
* private! builds an http document describing a headers reason.
*
* @param Error Error name.
* @param Description Errors description.
* @return A string with the HTML description body
*/
private String formErrorBody(String Error,String Description)
{
String out;
//Generate Error Body
out ="<HTML><HEAD><TITLE>";
out += Error ;
out +="</TITLE></HEAD>";
out +="<BODY><H2>" + Error +"</H2>\n";
out +="</P></H3>";
out += Description;
out +="</BODY></HTML>";
return out;
}
/**
* builds an http document describing an error.
*
* @param Error Error name.
* @param Description Errors description.
* @return A string with the HTML description body
*/
private String formError(String Error, String Description)
{
/* A HTTP RESPONCE HEADER LOOKS ALOT LIKE:
*
* HTTP/1.0 200 OK
* Date: Wednesday, 02-Feb-94 23:04:12 GMT
* Server: NCSA/1.1
* MIME-version: 1.0
* Last-modified: Monday, 15-Nov-93 23:33:16 GMT
* Content-type: text/html
* Content-length: 2345
* \r\n
*/
String body=formErrorBody(Error,Description);
String header =new String();
header +=HTTP_PROTOCOL +" " + Error + CR;
header +="Server: " + HTTP_SERVER + CR;
header +="MIME-version: 1.0" + CR;
header +="Content-type: text/html" + CR;
if (0 < lastModified.length())
header +="Last-Modified: " + lastModified +CR;
header +="Content-Length: " + String.valueOf(body.length())+ CR;
header += CR;
header += body;
return header;
}
/**
* Indicates a new file was created.
*
* @return The header in a string;
*/
public String formCreated()
{
return formError("201 Created","Object was created");
}
/**
* Indicates the document was accepted.
*
* @return The header in a string;
*/
public String formAccepted()
{
return formError("202 Accepted","Object checked in");
}
/**
* Indicates only a partial responce was sent.
*
* @return The header in a string;
*/
public String formPartial()
{
return formError("203 Partial","Only partail document available");
}
/**
* Indicates a requested URL has moved to a new address or name.
*
* @return The header in a string;
*/
public String formMoved()
{
//300 codes tell client to do actions
return formError("301 Moved","File has moved");
}
/**
* Never seen this used.
*
* @return The header in a string;
*/
public String formFound()
{
return formError("302 Found","Object was found");
}
/**
* The requested method is not implemented by the server.
*
* @return The header in a string;
*/
public String formMethod()
{
return formError("303 Method unseported","Method unseported");
}
/**
* Indicates remote copy of the requested object is current.
*
* @return The header in a string;
*/
public String formNotModified()
{
return formError("304 Not modified","Use local copy");
}
/**
* Client not otherized for the request.
*
* @return The header in a string;
*/
public String formUnautorized()
{
return formError("401 Unathorized","Unathorized use of this service");
}
/**
* Payment is required for service.
*
* @return The header in a string;
*/
public String formPaymentNeeded()
{
return formError("402 Payment required","Payment is required");
}
/**
* Client if forbidden to get the request service.
*
* @return The header in a string;
*/
public String formForbidden()
{
return formError("403 Forbidden","You need permission for this service");
}
/**
* The requested object was not found.
*
* @return The header in a string;
*/
public String formNotFound()
{
return formError("404 Not_found","Requested object was not found");
}
/**
* The server had a problem and could not fulfill the request.
*
* @return The header in a string;
*/
public String formInternalError()
{
return formError("500 Internal server error","Server broke");
}
/**
* Server does not do the requested feature.
*
* @return The header in a string;
*/
public String formNotImplemented()
{
return formError("501 Method not implemented","Service not implemented, programer was lazy");
}
/**
* Server is overloaded, client should try again latter.
*
* @return The header in a string;
*/
public String formOverloaded()
{
return formError("502 Server overloaded","Try again latter");
}
/**
* Indicates the request took to long.
*
* @return The header in a string;
*/
public String formTimeout()
{
return formError("503 Gateway timeout","The connection timed out");
}
/**
* Indicates the client's proxies could not locate a server.
*
* @return The header in a string;
*/
public String formServerNotFound()
{
return formError("503 Gateway timeout","The requested server was not found");
}
/**
* Indicates the client is not allowed to access the object.
*
* @return The header in a string;
*/
public String formNotAllowed()
{
return formError("403 Access Denied","Access is not allowed");
}
}
1.1 jakarta-jmeter/src/org/apache/jmeter/protocol/http/proxy/HttpRequestHdr.java
Index: HttpRequestHdr.java
===================================================================
package org.apache.jmeter.protocol.http.proxy;
/******************************************************************
*** File HttpRequestHdr.java
***
***/
import java.io.InputStream;
import java.io.DataInputStream;
import java.util.StringTokenizer;
//
// Class: HttpRequestHdr
// Abstract: The headers of the client HTTP request.
//
public class HttpRequestHdr
{
/**
* Http Request method. Such as get or post.
*/
public String method = new String();
/**
* The requested url. The universal resource locator that
* hopefully uniquely describes the object or service the
* client is requesting.
*/
public String url = new String();
/**
* Version of http being used. Such as HTTP/1.0
*/
public String version = new String();
/**
* The client's browser's name.
*/
public String userAgent = new String();
/**
* The requesting documents that contained the url link.
*/
public String referer = new String();
/**
* A internet address date of the remote copy.
*/
public String ifModifiedSince = new String();
/**
* A list of mime types the client can accept.
*/
public String accept = new String();
/**
* The clients authorization. Don't belive it.
*/
public String authorization = new String();
/**
* The type of content following the request header.
* Normally there is no content and this is blank, however
* the post method usually does have a content and a content
* length.
*/
public String contentType = new String();
/**
* The length of the content following the header. Usually
* blank.
*/
public int contentLength = -1;
/**
* The content length of a remote copy of the requested object.
*/
public int oldContentLength = -1;
/**
* Anything in the header that was unrecognized by this class.
*/
public String unrecognized = new String();
/**
* Indicates that no cached versions of the requested object are
* to be sent. Usually used to tell proxy not to send a cached copy.
* This may also effect servers that are front end for data bases.
*/
public boolean pragmaNoCache = false;
static String CR ="\r\n";
/**
* Parses a http header from a stream.
*
* @param in The stream to parse.
* @return true if parsing sucsessfull.
*/
public boolean parse(InputStream In)
{
String CR ="\r\n";
/*
* Read by lines
*/
DataInputStream lines;
StringTokenizer tz;
try
{
lines = new DataInputStream(In);
tz = new StringTokenizer(lines.readLine());
}
catch (Exception e)
{
return false;
}
/*
* HTTP COMMAND LINE < <METHOD==get> <URL> <HTTP_VERSION> >
*/
method = getToken(tz).toUpperCase();
url = getToken(tz);
version= getToken(tz);
while (true)
{
try
{
tz = new StringTokenizer(lines.readLine());
}
catch (Exception e)
{
return false;
}
String Token = getToken(tz);
// look for termination of HTTP command
if (0 == Token.length())
break;
if (Token.equalsIgnoreCase("USER-AGENT:")) {
// line =<User-Agent: <Agent Description>>
userAgent = getRemainder(tz);
} else if (Token.equalsIgnoreCase("ACCEPT:")) {
// line=<Accept: <Type>/<Form>
// examp: Accept image/jpeg
accept += " " + getRemainder(tz);
} else if (Token.equalsIgnoreCase("REFERER:")) {
// line =<Referer: <URL>>
referer = getRemainder(tz);
} else if (Token.equalsIgnoreCase("PRAGMA:")) {
// Pragma: <no-cache>
Token = getToken(tz);
if (Token.equalsIgnoreCase("NO-CACHE"))
pragmaNoCache = true;
else
unrecognized += "Pragma:" + Token + " "
+getRemainder(tz) +"\n";
} else if (Token.equalsIgnoreCase("AUTHORIZATION:")) {
// Authenticate: Basic UUENCODED
authorization= getRemainder(tz);
} else if (Token.equalsIgnoreCase("IF-MODIFIED-SINCE:")) {
// line =<If-Modified-Since: <http date>
// *** Conditional GET replaces HEAD method ***
String str = getRemainder(tz);
int index = str.indexOf(";");
if (index == -1) {
ifModifiedSince =str;
} else {
ifModifiedSince =str.substring(0,index);
index = str.indexOf("=");
if (index != -1) {
str = str.substring(index+1);
oldContentLength =Integer.parseInt(str);
}
}
} else if (Token.equalsIgnoreCase("CONTENT-LENGTH:")) {
Token = getToken(tz);
contentLength =Integer.parseInt(Token);
} else if (Token.equalsIgnoreCase("CONTENT-TYPE:")) {
contentType = getRemainder(tz);
} else {
unrecognized += Token + " " + getRemainder(tz) + CR;
}
}
return true;
}
/*
* Rebuilds the header in a string
* @returns The header in a string.
*/
public String toString(boolean sendUnknowen) {
String Request;
if (0 == method.length())
method = "GET";
Request = method +" "+ url + " HTTP/1.0" + CR;
if (0 < userAgent.length())
Request +="User-Agent:" + userAgent + CR;
if (0 < referer.length())
Request+= "Referer:"+ referer + CR;
if (pragmaNoCache)
Request+= "Pragma: no-cache" + CR;
if (0 < ifModifiedSince.length())
Request+= "If-Modified-Since: " + ifModifiedSince + CR;
// ACCEPT TYPES //
if (0 < accept.length())
Request += "Accept: " + accept + CR;
else
Request += "Accept: */"+"* \r\n";
if (0 < contentType.length())
Request += "Content-Type: " + contentType + CR;
if (0 < contentLength)
Request += "Content-Length: " + contentLength + CR;
if (0 != authorization.length())
Request += "Authorization: " + authorization + CR;
if (sendUnknowen) {
if (0 != unrecognized.length())
Request += unrecognized;
}
Request += CR;
return Request;
}
/**
* (Re)builds the header in a string.
*
* @returns The header in a string.
*/
public String toString() {
return toString(true);
}
/**
* Returns the next token in a string
*
* @param tk String that is partially tokenized.
* @returns The remainder
*/
String getToken(StringTokenizer tk){
String str ="";
if (tk.hasMoreTokens())
str =tk.nextToken();
return str;
}
/**
* Returns the remainder of a tokenized string
*
* @param tk String that is partially tokenized.
* @returns The remainder
*/
String getRemainder(StringTokenizer tk){
String str ="";
if (tk.hasMoreTokens())
str =tk.nextToken();
while (tk.hasMoreTokens()){
str +=" " + tk.nextToken();
}
return str;
}
//
// Parsing Methods
//
/**
* Find the //server.name from an url.
*
* @return Servers internet name
*/
public String serverName()
{
// chop to "server.name:x/thing"
String str = url;
int i = str.indexOf("//");
if (i< 0) return "";
str = str.substring(i+2);
// chop to server.name:xx
i = str.indexOf("/");
if (0 < i) str = str.substring(0,i);
// chop to server.name
i = str.indexOf(":");
if (0 < i) str = str.substring(0,i);
return str;
}
/**
* Find the :PORT form http://server.ect:PORT/some/file.xxx
*
* @return Servers internet name
*/
public int serverPort()
{
String str = url;
// chop to "server.name:x/thing"
int i = str.indexOf("//");
if (i< 0) return 80;
str = str.substring(i+2);
// chop to server.name:xx
i = str.indexOf("/");
if (0 < i) str = str.substring(0,i);
// chop XX
i = str.indexOf(":");
if (0 < i)
{
return Integer.parseInt(str.substring(i+1).trim());
}
return 80;
}
/**
* Find the /some/file.xxxx form http://server.ect:PORT/some/file.xxx
*
* @return the deproxied url
*/
public String serverUrl()
{
String str = url;
int i = str.indexOf("//");
if (i< 0) return str;
str = str.substring(i+2);
i = str.indexOf("/");
if (i< 0) return str;
return str.substring(i);
}
}
1.1 jakarta-jmeter/src/org/apache/jmeter/protocol/http/proxy/Proxy.java
Index: Proxy.java
===================================================================
package org.apache.jmeter.protocol.http.proxy;
import java.net.*;
import java.io.*;
import java.util.*;
import org.apache.jmeter.protocol.http.config.UrlConfig;
import org.apache.jmeter.save.xml.TagHandler;
//
// Class: Proxy
// Abstract: Thread to handle one client request. get the requested
// object from the web server or from the cache, and delivers
// the bits to client.
//
/**
* Description of the Class
*
*@author mike
*@created June 8, 2001
*/
public class Proxy extends Thread
{
//
// Member variables
//
Socket ClientSocket = null;
// Socket to client
Socket SrvrSocket = null;
// Socket to web server
Cache cache = null;
// Static cache manager object
String localHostName = null;
// Local machine name
String localHostIP = null;
// Local machine IP address
String adminPath = null;
// Path of admin applet
Config config = null;
// Config object
UrlConfig urlConfig = null;
// UrlConfig object for saving test cases
//
// Public member methods
//
//
// Constructor
//
Proxy(Socket clientSocket, Cache CacheManager, Config configObject)
{
//
// Initialize member variables
//
config = configObject;
ClientSocket = clientSocket;
cache = CacheManager;
localHostName = config.getLocalHost();
localHostIP = config.getLocalIP();
adminPath = config.getAdminPath();
}
//
// run - Main work is done here:
//
/**
* Main processing method for the Proxy object
*/
public void run()
{
String serverName = "";
URL url;
byte line[];
HttpRequestHdr request = new HttpRequestHdr();
HttpReplyHdr reply = new HttpReplyHdr();
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
boolean TakenFromCache = false;
boolean isCachable = false;
try
{
//
// Read HTTP Request from client
//
request.parse(ClientSocket.getInputStream());
createUrlConfig(request);
config.increaseFilesCached();
url = new URL(request.url);
System.out.println("Request = " + url);
//
// Send Web page with applet to administrator
//
if (url.getFile().equalsIgnoreCase("/admin") &&
(url.getHost().equalsIgnoreCase(localHostName) ||
url.getHost().equalsIgnoreCase(localHostIP)))
{
sendAppletWebPage();
return;
}
//
// Send Applet Files to administrator
//
if ((url.getHost().equalsIgnoreCase(localHostName) ||
url.getHost().equalsIgnoreCase(localHostIP)))
{
sendAppletClass(url.getFile());
return;
}
//
// Check if accessing the URL is allowed by administrator
//
String[] denied = config.getDeniedHosts();
for (int i = 0; i < denied.length; i++)
{
if (url.toString().indexOf(denied[i]) != -1)
{
System.out.println("Access not allowed...");
DataOutputStream out =
new DataOutputStream(ClientSocket.getOutputStream());
out.writeBytes(reply.formNotAllowed());
out.flush();
ClientSocket.close();
return;
}
}
serverName = url.getHost();
System.out.println("Miss! Forwarding to server " +
serverName + "...");
config.increaseMisses();
SrvrSocket = new Socket(request.serverName(),
request.serverPort());
request.url = request.serverUrl();
DataOutputStream srvOut =
new DataOutputStream(SrvrSocket.getOutputStream());
//
// Send the url to web server (or father proxy)
//
srvOut.writeBytes(request.toString(false));
srvOut.flush();
//
// Send data to server (needed for post method)
//
StringBuffer buff = new StringBuffer();
int readValue;
for (int i = 0; i < request.contentLength; i++)
{
readValue = ClientSocket.getInputStream().read();
buff.append((char)readValue);
SrvrSocket.getOutputStream().write(readValue);
}
SrvrSocket.getOutputStream().flush();
urlConfig.parseArguments(buff.toString());
saveUrlConfig();
// Third, check reply headers (we must read first
// line of headers for that).
DataInputStream Din =
new DataInputStream(SrvrSocket.getInputStream());
DataOutputStream Dout =
new DataOutputStream(ClientSocket.getOutputStream());
String str = Din.readLine();
StringTokenizer s = new StringTokenizer(str);
String retCode = s.nextToken();
// first token is HTTP protocol
retCode = s.nextToken();
// second is return code
//
// First line was read - send it to client and cache it
//
String tempStr = new String(str + "\r\n");
Dout.writeBytes(tempStr);
//
// Read next lines in reply header, send them to
// client and cache them
//
if (str.length() > 0)
{
while (true)
{
str = Din.readLine();
tempStr = new String(str + "\r\n");
// Send bits to client
Dout.writeBytes(tempStr);
if (str.length() <= 0)
{
break;
}
}
}
Dout.flush();
//
// With the HTTP reply body do:
// (1) Send it to client.
// (2) Cache it.
//
InputStream in = SrvrSocket.getInputStream();
OutputStream out = ClientSocket.getOutputStream();
byte data[] = new byte[2000];
int count;
while ((count = in.read(data)) > 0)
{
// Send bits to client
out.write(data, 0, count);
}
out.flush();
}
catch (UnknownHostException uhe)
{
//
// Requested Server could not be located
//
System.out.println("Server Not Found.");
try
{
// Notify client that server not found
DataOutputStream out =
new DataOutputStream(ClientSocket.getOutputStream());
out.writeBytes(reply.formServerNotFound());
out.flush();
}
catch (Exception uhe2)
{
}
}
catch (Exception e)
{
e.printStackTrace();
try
{
if (TakenFromCache)
{
fileInputStream.close();
}
else if (isCachable)
{
fileOutputStream.close();
}
// Notify client that internal error accured in proxy
DataOutputStream out =
new DataOutputStream(ClientSocket.getOutputStream());
out.writeBytes(reply.formTimeout());
out.flush();
}
catch (Exception uhe2)
{
}
}
finally
{
try
{
ClientSocket.getOutputStream().flush();
ClientSocket.close();
}
catch (Exception e)
{
}
}
}
//
// Private methods
//
//
// Send to administrator web page containing reference to applet
//
private void sendAppletWebPage()
{
System.out.println("Sending the applet...");
String page = "";
try
{
File appletHtmlPage = new File(config.getAdminPath() +
File.separator + "Admin.html");
DataInputStream in = new DataInputStream(new FileInputStream(appletHtmlPage));
String s = null;
while ((s = in.readLine()) != null)
{
page += s;
}
page = page.substring(0, page.indexOf("PORT")) +
config.getAdminPort() +
page.substring(page.indexOf("PORT") + 4);
in.close();
DataOutputStream out = new DataOutputStream(ClientSocket.getOutputStream());
out.writeBytes(page);
out.flush();
out.close();
}
catch (Exception e)
{
System.out.println("Error: can't open applet html page");
}
}
//
// Send the applet to administrator
//
private void sendAppletClass(String className)
{
try
{
byte data[] = new byte[2000];
int count;
HttpReplyHdr reply = new HttpReplyHdr();
File appletFile = new File(adminPath + File.separatorChar + className);
long length = appletFile.length();
FileInputStream in = new FileInputStream(appletFile);
DataOutputStream out = new DataOutputStream(ClientSocket.getOutputStream());
out.writeBytes(reply.formOk("application/octet-stream", length));
while (-1 < (count = in.read(data)))
{
out.write(data, 0, count);
}
out.flush();
in.close();
out.close();
}
catch (Exception e)
{
}
}
private void createUrlConfig(HttpRequestHdr request)
{
System.out.println("extra stuff = " + request.unrecognized);
System.out.println("Everything = " + request.toString(false));
urlConfig = new UrlConfig();
urlConfig.setDomain(request.serverName());
urlConfig.setMethod(request.method);
urlConfig.setPath(request.serverUrl());
urlConfig.setName(urlConfig.getPath());
urlConfig.setProtocol(request.url.substring(0, request.url.indexOf(":")));
urlConfig.setPort(request.serverPort());
}
private void saveUrlConfig()
{
try
{
String name;
int index = urlConfig.getName().lastIndexOf("/");
if (index > -1)
{
name = urlConfig.getName().substring(index + 1);
}
else
{
name = urlConfig.getName();
}
FileWriter out = new FileWriter(System.getProperty("user.dir") +
File.separator + config.getJmxScriptDir() + File.separator + name +
"_" + config.getFilesCached() + ".jmx");
TagHandler handler = (TagHandler) urlConfig.getTagHandlerClass().newInstance();
handler.startSave(out);
handler.save(urlConfig, out);
out.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
1.12 +20 -2 jakarta-jmeter/src/org/apache/jmeter/protocol/http/sampler/HTTPSampler.java
Index: HTTPSampler.java
===================================================================
RCS file: /home/cvs/jakarta-jmeter/src/org/apache/jmeter/protocol/http/sampler/HTTPSampler.java,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- HTTPSampler.java 2001/06/07 23:38:47 1.11
+++ HTTPSampler.java 2001/06/14 00:28:21 1.12
@@ -72,8 +72,8 @@
* HTTP requests, including cookies and authentication.
*
*@author Michael Stover
- *@created $Date: 2001/06/07 23:38:47 $
- *@version $Revision: 1.11 $
+ *@created $Date: 2001/06/14 00:28:21 $
+ *@version $Revision: 1.12 $
***********************************************************/
public class HTTPSampler implements Sampler
{
@@ -169,6 +169,23 @@
}
}
+ private void setConnectionHeaders(HttpURLConnection conn, URL u, HeaderManager headerManager)
+ {
+ if(headerManager != null)
+ {
+ Collection headers = headerManager.getHeaders();
+ if(headers != null)
+ {
+ Iterator i = headers.iterator();
+ while(i.hasNext())
+ {
+ Header header = (Header)i.next();
+ conn.setRequestProperty(header.getName(),header.getValue());
+ }
+ }
+ }
+ }
+
private void setConnectionAuthorization(HttpURLConnection conn, URL u, AuthManager authManager)
{
if (authManager != null)
@@ -241,6 +258,7 @@
conn = (HttpURLConnection)u.openConnection();
conn.setFollowRedirects(false);
conn.setRequestMethod((String)url.getProperty(UrlConfig.METHOD));
+ setConnectionHeaders(conn,u,(HeaderManager)e.getConfigElement(HeaderManager.class));
setConnectionCookie(conn, u, (CookieManager)e.getConfigElement(CookieManager.class));
setConnectionAuthorization(conn, u, (AuthManager)e.getConfigElement(AuthManager.class));
// if POSTing data, write data to output stream
1.1 jakarta-jmeter/src/org/apache/jmeter/protocol/http/save/HeaderManagerHandler.java
Index: HeaderManagerHandler.java
===================================================================
/*
* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" and
* "Apache JMeter" must not be used to endorse or promote products
* derived from this software without prior written permission. For
* written permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* "Apache JMeter", nor may "Apache" appear in their name, without
* prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.jmeter.protocol.http.save;
import org.xml.sax.Attributes;
import java.util.*;
import org.apache.jmeter.save.Saveable;
import java.io.Writer;
import org.apache.jmeter.save.xml.*;
import org.apache.jmeter.save.handlers.JMeterHandler;
import org.apache.jmeter.protocol.http.control.*;
/**
* Title:
* Description:
* Copyright: Copyright (c) 2001
* Company:
* @author Giacomo Pati
* @version 1.0
*/
public class HeaderManagerHandler extends TagHandler
{
private HeaderManager headerManager;
public HeaderManagerHandler()
{
}
public void save(Saveable cm, Writer out) throws java.io.IOException
{
HeaderManager headers = (HeaderManager)cm;
out.write("<HeaderManager name=\"");
out.write(JMeterHandler.convertToXML(headers.getName()));
out.write("\">\n");
JMeterHandler.writeObjects(headers.getHeaders(),out);
out.write("</HeaderManager>\n");
}
public void setAtts(Attributes atts) throws java.lang.Exception
{
headerManager = new HeaderManager();
headerManager.setName(atts.getValue("name"));
}
public String getPrimaryTagName()
{
return "HeaderManager";
}
public void HeaderManagerTagEnd()
{
List headers = xmlParent.takeChildObjects(this);
Iterator iter = headers.iterator();
while (iter.hasNext())
{
headerManager.add((Header)((TagHandler)iter.next()).getModel());
}
}
public Object getModel()
{
return headerManager;
}
}
---------------------------------------------------------------------
To unsubscribe, e-mail: jmeter-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: jmeter-dev-help@jakarta.apache.org