Update to htmlparser 2.0

git-svn-id: https://svn.apache.org/repos/asf/jakarta/jmeter/branches/rel-2-2@512075 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Sebastian Bazley 2007-02-26 23:28:57 +00:00
parent 468b472050
commit 30ff6849e5
9 changed files with 333 additions and 695 deletions

213
LICENSE(htmlparser).txt Normal file
View File

@ -0,0 +1,213 @@
Common Public License Version 1.0
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC
LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
1. DEFINITIONS
"Contribution" means:
a) in the case of the initial Contributor, the initial code and
documentation distributed under this Agreement, and
b) in the case of each subsequent Contributor:
i) changes to the Program, and
ii) additions to the Program;
where such changes and/or additions to the Program originate from and are
distributed by that particular Contributor. A Contribution 'originates' from a
Contributor if it was added to the Program by such Contributor itself or anyone
acting on such Contributor's behalf. Contributions do not include additions to
the Program which: (i) are separate modules of software distributed in
conjunction with the Program under their own license agreement, and (ii) are not
derivative works of the Program.
"Contributor" means any person or entity that distributes the Program.
"Licensed Patents " mean patent claims licensable by a Contributor which are
necessarily infringed by the use or sale of its Contribution alone or when
combined with the Program.
"Program" means the Contributions distributed in accordance with this Agreement.
"Recipient" means anyone who receives the Program under this Agreement,
including all Contributors.
2. GRANT OF RIGHTS
a) Subject to the terms of this Agreement, each Contributor hereby grants
Recipient a non-exclusive, worldwide, royalty-free copyright license to
reproduce, prepare derivative works of, publicly display, publicly perform,
distribute and sublicense the Contribution of such Contributor, if any, and such
derivative works, in source code and object code form.
b) Subject to the terms of this Agreement, each Contributor hereby grants
Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed
Patents to make, use, sell, offer to sell, import and otherwise transfer the
Contribution of such Contributor, if any, in source code and object code form.
This patent license shall apply to the combination of the Contribution and the
Program if, at the time the Contribution is added by the Contributor, such
addition of the Contribution causes such combination to be covered by the
Licensed Patents. The patent license shall not apply to any other combinations
which include the Contribution. No hardware per se is licensed hereunder.
c) Recipient understands that although each Contributor grants the licenses
to its Contributions set forth herein, no assurances are provided by any
Contributor that the Program does not infringe the patent or other intellectual
property rights of any other entity. Each Contributor disclaims any liability to
Recipient for claims brought by any other entity based on infringement of
intellectual property rights or otherwise. As a condition to exercising the
rights and licenses granted hereunder, each Recipient hereby assumes sole
responsibility to secure any other intellectual property rights needed, if any.
For example, if a third party patent license is required to allow Recipient to
distribute the Program, it is Recipient's responsibility to acquire that license
before distributing the Program.
d) Each Contributor represents that to its knowledge it has sufficient
copyright rights in its Contribution, if any, to grant the copyright license set
forth in this Agreement.
3. REQUIREMENTS
A Contributor may choose to distribute the Program in object code form under its
own license agreement, provided that:
a) it complies with the terms and conditions of this Agreement; and
b) its license agreement:
i) effectively disclaims on behalf of all Contributors all warranties and
conditions, express and implied, including warranties or conditions of title and
non-infringement, and implied warranties or conditions of merchantability and
fitness for a particular purpose;
ii) effectively excludes on behalf of all Contributors all liability for
damages, including direct, indirect, special, incidental and consequential
damages, such as lost profits;
iii) states that any provisions which differ from this Agreement are offered
by that Contributor alone and not by any other party; and
iv) states that source code for the Program is available from such
Contributor, and informs licensees how to obtain it in a reasonable manner on or
through a medium customarily used for software exchange.
When the Program is made available in source code form:
a) it must be made available under this Agreement; and
b) a copy of this Agreement must be included with each copy of the Program.
Contributors may not remove or alter any copyright notices contained within the
Program.
Each Contributor must identify itself as the originator of its Contribution, if
any, in a manner that reasonably allows subsequent Recipients to identify the
originator of the Contribution.
4. COMMERCIAL DISTRIBUTION
Commercial distributors of software may accept certain responsibilities with
respect to end users, business partners and the like. While this license is
intended to facilitate the commercial use of the Program, the Contributor who
includes the Program in a commercial product offering should do so in a manner
which does not create potential liability for other Contributors. Therefore, if
a Contributor includes the Program in a commercial product offering, such
Contributor ("Commercial Contributor") hereby agrees to defend and indemnify
every other Contributor ("Indemnified Contributor") against any losses, damages
and costs (collectively "Losses") arising from claims, lawsuits and other legal
actions brought by a third party against the Indemnified Contributor to the
extent caused by the acts or omissions of such Commercial Contributor in
connection with its distribution of the Program in a commercial product
offering. The obligations in this section do not apply to any claims or Losses
relating to any actual or alleged intellectual property infringement. In order
to qualify, an Indemnified Contributor must: a) promptly notify the Commercial
Contributor in writing of such claim, and b) allow the Commercial Contributor to
control, and cooperate with the Commercial Contributor in, the defense and any
related settlement negotiations. The Indemnified Contributor may participate in
any such claim at its own expense.
For example, a Contributor might include the Program in a commercial product
offering, Product X. That Contributor is then a Commercial Contributor. If that
Commercial Contributor then makes performance claims, or offers warranties
related to Product X, those performance claims and warranties are such
Commercial Contributor's responsibility alone. Under this section, the
Commercial Contributor would have to defend claims against the other
Contributors related to those performance claims and warranties, and if a court
requires any other Contributor to pay any damages as a result, the Commercial
Contributor must pay those damages.
5. NO WARRANTY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each
Recipient is solely responsible for determining the appropriateness of using and
distributing the Program and assumes all risks associated with its exercise of
rights under this Agreement, including but not limited to the risks and costs of
program errors, compliance with applicable laws, damage to or loss of data,
programs or equipment, and unavailability or interruption of operations.
6. DISCLAIMER OF LIABILITY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
PROFITS), 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 OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS
GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
7. GENERAL
If any provision of this Agreement is invalid or unenforceable under applicable
law, it shall not affect the validity or enforceability of the remainder of the
terms of this Agreement, and without further action by the parties hereto, such
provision shall be reformed to the minimum extent necessary to make such
provision valid and enforceable.
If Recipient institutes patent litigation against a Contributor with respect to
a patent applicable to software (including a cross-claim or counterclaim in a
lawsuit), then any patent licenses granted by that Contributor to such Recipient
under this Agreement shall terminate as of the date such litigation is filed. In
addition, if Recipient institutes patent litigation against any entity
(including a cross-claim or counterclaim in a lawsuit) alleging that the Program
itself (excluding combinations of the Program with other software or hardware)
infringes such Recipient's patent(s), then such Recipient's rights granted under
Section 2(b) shall terminate as of the date such litigation is filed.
All Recipient's rights under this Agreement shall terminate if it fails to
comply with any of the material terms or conditions of this Agreement and does
not cure such failure in a reasonable period of time after becoming aware of
such noncompliance. If all Recipient's rights under this Agreement terminate,
Recipient agrees to cease use and distribution of the Program as soon as
reasonably practicable. However, Recipient's obligations under this Agreement
and any licenses granted by Recipient relating to the Program shall continue and
survive.
Everyone is permitted to copy and distribute copies of this Agreement, but in
order to avoid inconsistency the Agreement is copyrighted and may only be
modified in the following manner. The Agreement Steward reserves the right to
publish new versions (including revisions) of this Agreement from time to time.
No one other than the Agreement Steward has the right to modify this Agreement.
IBM is the initial Agreement Steward. IBM may assign the responsibility to serve
as the Agreement Steward to a suitable separate entity. Each new version of the
Agreement will be given a distinguishing version number. The Program (including
Contributions) may always be distributed subject to the version of the Agreement
under which it was received. In addition, after a new version of the Agreement
is published, Contributor may elect to distribute the Program (including its
Contributions) under the new version. Except as expressly stated in Sections
2(a) and 2(b) above, Recipient receives no rights or licenses to the
intellectual property of any Contributor under this Agreement, whether
expressly, by implication, estoppel or otherwise. All rights in the Program not
expressly granted under this Agreement are reserved.
This Agreement is governed by the laws of the State of New York and the
intellectual property laws of the United States of America. No party to this
Agreement will bring a legal action under this Agreement more than one year
after the cause of action arose. Each party waives its rights to a jury trial in
any resulting litigation.

3
NOTICE
View File

@ -32,8 +32,7 @@ For detailed information about HTMLParser, the project is
hosted on Sourceforge at http://htmlparser.sourceforge.net/
The developers of JMeter and Apache are grateful to the developers
of HTMLParser for giving Apache Software Foundation a non-exclusive
license to use HTMLParser under Apache 1 license.
of HTMLParser for re-releasing htmlparser under CPL V1.0
HTMLParser was originally created by Somik Raha in 2000.
Derrick Oswald is the current lead developer and was kind

160
build.xml
View File

@ -140,12 +140,6 @@
<sourcePath path="${src.jms}" />
<sourcePath path="${src.report}" />
<!--
Uncomment the following 2 lines to include htmlparser in the analysis
<class location="${lib.dir}/htmlparser.jar" />
<sourcePath path="${src.htmlparser}" />
-->
<auxClasspath>
<fileset dir="${lib.dir}">
<include name="*.jar"/>
@ -194,8 +188,6 @@
<property name="src.functions" value="src/functions"/>
<property name="src.jorphan" value="src/jorphan"/>
<property name="src.ldap" value="src/protocol/ldap"/>
<property name="src.htmlparser" value="src/htmlparser"/>
<property name="src.htmlparser16" value="src/htmlparser16"/>
<property name="src.tcp" value="src/protocol/tcp"/>
<property name="src.examples" value="src/examples"/>
<property name="src.mail" value="src/protocol/mail"/>
@ -221,8 +213,6 @@
<pathelement location="${src.junit}"/>
<pathelement location="${src.jorphan}"/>
<pathelement location="${src.ldap}"/>
<pathelement location="${src.htmlparser}"/>
<pathelement location="${src.htmlparser16}"/>
<pathelement location="${src.tcp}"/>
<pathelement location="${src.examples}"/>
<pathelement location="${src.mail}"/>
@ -245,8 +235,6 @@
<property name="build.jorphan" value="build/jorphan"/>
<property name="build.ldap" location="build/protocol/ldap"/>
<property name="build.mail" location="build/protocol/mail"/>
<property name="build.htmlparser" location="build/htmlparser"/>
<property name="build.htmlparser16" location="build/htmlparser16"/>
<property name="build.tcp" location="build/protocol/tcp"/>
<property name="build.examples" location="build/examples"/>
<property name="build.monitor.components" location="build/monitor/components"/>
@ -399,10 +387,9 @@
-->
<!-- Build classpath (includes the optional jar directory) -->
<!-- Exclude the htmlparser jars to prevent clashes between versions -->
<path id="classpath">
<fileset dir="${lib.dir}" includes="*.jar" excludes="htmlparser*.jar"/>
<fileset dir="${lib.opt}" includes="*.jar" excludes="htmlparser*.jar"/>
<fileset dir="${lib.dir}" includes="*.jar"/>
<fileset dir="${lib.opt}" includes="*.jar"/>
</path>
<!-- Anakia classpath -->
@ -454,11 +441,6 @@
<available classname="bsh.Interpreter" property="beanshell.present">
<classpath refid="classpath"/>
</available>
<available classname="org.htmlparser.Tag" property="htmlparser16.present">
<classpath>
<fileset dir="${lib.opt}" includes="htmlparser1_6.jar"/>
</classpath>
</available>
<available classname="javax.jms.Message" property="jms.present">
<classpath refid="classpath"/>
</available>
@ -479,16 +461,12 @@
<echo message="Classes for BeanShell support not found in classpath"/>
</target>
<target name="htmlparser16-message" depends="check-libs" unless="htmlparser16.present">
<echo message="Classes for HTMLParser 1.6 support not found in classpath"/>
</target>
<target name="jms-message" depends="check-libs" unless="jms.present">
<echo message="Classes for JMS support not found in classpath"/>
</target>
<target name="report-missing-libs"
depends="ssl-message,mail-message,beanshell-message,htmlparser16-message,jms-message"
depends="ssl-message,mail-message,beanshell-message,jms-message"
/>
<!--
@ -551,10 +529,9 @@
</javac>
</target>
<target name="compile-http" depends="compile-jorphan,compile-core,compile-components,compile-htmlparser" description="Compile components specific to HTTP sampling.">
<target name="compile-http" depends="compile-jorphan,compile-core,compile-components" description="Compile components specific to HTTP sampling.">
<mkdir dir="${build.http}"/>
<!-- Directory needs to exist, or jar will fail -->
<mkdir dir="${build.htmlparser16}"/>
<javac srcdir="${src.http}" destdir="${build.http}" source="${src.java.version}" optimize="${optimize}" debug="on" target="${target.java.version}" deprecation="${deprecation}" encoding="${encoding}">
<include name="**/*.java"/>
<exclude name="org/apache/jmeter/protocol/http/sampler/WebServiceSampler.java" unless="javamail.complete"/>
@ -563,27 +540,11 @@
<pathelement location="${build.jorphan}"/>
<pathelement location="${build.core}"/>
<pathelement location="${build.components}"/>
<pathelement location="${build.htmlparser}"/>
<path refid="classpath"/>
</classpath>
</javac>
</target>
<target name="compile-htmlparser16" depends="compile-http,compile-htmlparser" description="Compile htmlparser 1.6 support" if="htmlparser16.present">
<mkdir dir="${build.htmlparser16}"/>
<javac srcdir="${src.htmlparser16}" destdir="${build.htmlparser16}" optimize="${optimize}" debug="on" source="${src.java.version}" target="${target.java.version}" deprecation="${deprecation}" encoding="${encoding}">
<include name="**/*.java"/>
<classpath>
<pathelement location="${build.jorphan}"/>
<pathelement location="${build.core}"/>
<pathelement location="${build.http}"/>
<fileset dir="${lib.opt}" includes="htmlparser1_6.jar"/>
<pathelement location="${}"/>
<path refid="classpath"/>
</classpath>
</javac>
</target>
<target name="compile-tests" description="Compile test components only">
<mkdir dir="${build.test}"/>
<javac srcdir="${src.test}" destdir="${build.test}" source="${src.java.version}" optimize="${optimize}" debug="on" target="${target.java.version}" deprecation="${deprecation}" encoding="${encoding}">
@ -711,7 +672,7 @@
</javac>
</target>
<target name="compile-protocols" depends="compile-http,compile-htmlparser16,compile-ftp,compile-jdbc,compile-java,compile-ldap,compile-mail,compile-tcp" description="Compile all protocol-specific components."/>
<target name="compile-protocols" depends="compile-http,compile-ftp,compile-jdbc,compile-java,compile-ldap,compile-mail,compile-tcp" description="Compile all protocol-specific components."/>
<target name="compile-examples" depends="compile-jorphan,compile-core" description="Compile example components.">
<mkdir dir="${build.examples}"/>
@ -781,18 +742,6 @@
</rmic>
</target>
<target name="compile-htmlparser" depends="" description="Compile htmlparser.">
<mkdir dir="${build.htmlparser}"/>
<javac srcdir="${src.htmlparser}" destdir="${build.htmlparser}" optimize="${optimize}" debug="on" source="${src.java.version}" target="${target.java.version}" deprecation="${deprecation}" encoding="${encoding}">
<include name="**/*.java"/>
<classpath>
<pathelement location="${build.jorphan}"/>
<pathelement location="${build.core}"/>
<path refid="classpath"/>
</classpath>
</javac>
</target>
<target name="compile-jms" depends="compile-jorphan,compile-core,compile-components"
description="Compile components specific to JMS sampling."
if="jms.present">
@ -808,7 +757,7 @@
</target>
<target name="compile"
depends="compile-core,compile-components,compile-functions,compile-protocols,compile-rmi,compile-htmlparser,compile-monitor,compile-junit,compile-jms,compile-report"
depends="compile-core,compile-components,compile-functions,compile-protocols,compile-rmi,compile-monitor,compile-junit,compile-jms,compile-report"
description="Compile everything."/>
<target name="package" depends="compile, package-only"
@ -899,10 +848,9 @@ some of its classes - at present JMeter can only run from jar files.
</manifest>
</jar>
<!-- http without HtmlParserHTMLParser -->
<!-- http -->
<jar jarfile="${dest.jar}/ApacheJMeter_http.jar" manifest="MANIFEST">
<fileset dir="${build.http}" includes="**/*.class"
excludes="**/HtmlParserHTMLParser.class"/>
<fileset dir="${build.http}" includes="**/*.class"/>
<fileset dir="${src.http}" includes="**/*.properties" />
<metainf dir="." includes="LICENSE,NOTICE"/>
<manifest>
@ -912,30 +860,6 @@ some of its classes - at present JMeter can only run from jar files.
<attribute name="X-Compile-Target-JDK" value="${target.java.version}"/>
</manifest>
</jar>
<!-- HtmlParserHTMLParser -->
<jar jarfile="${lib.dir}/htmlparserparser.jar" manifest="MANIFEST">
<fileset dir="${build.http}" includes="**/HtmlParserHTMLParser.class"/>
<metainf dir="." includes="LICENSE,NOTICE"/>
<manifest>
<attribute name="Built-By" value="${user.name}"/>
<attribute name="Implementation-Version" value="${jmeter.version}"/>
<attribute name="X-Compile-Source-JDK" value="${src.java.version}"/>
<attribute name="X-Compile-Target-JDK" value="${target.java.version}"/>
</manifest>
</jar>
<!-- HtmlParserHTMLParser16 -->
<jar jarfile="${lib.dir}/htmlparserparser16.jar" manifest="MANIFEST">
<fileset dir="${build.htmlparser16}" includes="**/*.class"/>
<metainf dir="." includes="LICENSE,NOTICE"/>
<manifest>
<attribute name="Built-By" value="${user.name}"/>
<attribute name="Implementation-Version" value="${jmeter.version}"/>
<attribute name="X-Compile-Source-JDK" value="${src.java.version}"/>
<attribute name="X-Compile-Target-JDK" value="${target.java.version}"/>
</manifest>
</jar>
<!-- ftp -->
<jar jarfile="${dest.jar}/ApacheJMeter_ftp.jar" manifest="MANIFEST">
@ -1081,19 +1005,7 @@ some of its classes - at present JMeter can only run from jar files.
<attribute name="X-Compile-Target-JDK" value="${target.java.version}"/>
</manifest>
</jar>
<jar jarfile="${lib.dir}/htmlparser.jar" manifest="MANIFEST">
<fileset dir="${build.htmlparser}" includes="**/*.class" excludes="org/htmlparser/tests/**/*.class"/>
<fileset dir="${src.htmlparser}" includes="**/*.properties" />
<metainf dir="." includes="LICENSE,NOTICE"/>
<manifest>
<attribute name="Built-By" value="${user.name}"/>
<attribute name="Implementation-Version" value="${jmeter.version}"/>
<attribute name="X-Compile-Source-JDK" value="${src.java.version}"/>
<attribute name="X-Compile-Target-JDK" value="${target.java.version}"/>
</manifest>
</jar>
<jar jarfile="${lib.dir}/jorphan.jar" manifest="MANIFEST">
<fileset dir="${build.jorphan}" includes="**/*.class"/>
<fileset dir="${src.jorphan}" includes="**/*.properties"/>
@ -1143,7 +1055,6 @@ some of its classes - at present JMeter can only run from jar files.
<property name="javamail.complete" value="assume"/>
<property name="beanshell.present" value="assume"/>
<property name="jms.present" value="assume"/>
<!--property name="htmlparser16.present" value="assume"/-->
</target>
<!-- list of files needed for a binary distribution (excluding library files) -->
@ -1172,9 +1083,8 @@ some of its classes - at present JMeter can only run from jar files.
<include name="${extras.dir}/**"/>
<include name="${lib.dir}/bshclient.jar"/>
<include name="${lib.dir}/jorphan.jar"/>
<include name="${lib.dir}/htmlparser.jar"/>
<include name="${lib.dir}/htmlparserparser.jar"/>
<include name="${lib.dir}/htmlparserparser16.jar"/>
<include name="${lib.dir}/htmllexer-2.0-20060923.jar"/>
<include name="${lib.dir}/htmlparser-2.0-20060923.jar"/>
<include name="${lib.dir}/junit/test.jar"/>
</patternset>
@ -1501,9 +1411,6 @@ some of its classes - at present JMeter can only run from jar files.
<target name="clean" description="Clean up to force a build from source.">
<delete file="${dest.jar.jmeter}/ApacheJMeter.jar"/>
<delete quiet="true" file="${lib.dir}/jorphan.jar"/>
<delete quiet="true" file="${lib.dir}/htmlparser.jar"/>
<delete quiet="true" file="${lib.dir}/htmlparserparser.jar"/>
<delete quiet="true" file="${lib.dir}/htmlparserparser16.jar"/>
<delete quiet="true">
<fileset dir="${dest.jar}"/>
</delete>
@ -1650,8 +1557,6 @@ some of its classes - at present JMeter can only run from jar files.
<classpath>
<fileset dir="${dest.jar}" includes="*.jar"/>
<pathelement location="${build.test}"/>
<fileset dir="${lib.dir}" includes="htmlparser.jar"/>
<fileset dir="${lib.dir}" includes="htmlparserparser.jar"/>
<path refid="classpath"/>
</classpath>
<sysproperty key="java.awt.headless" value="${test.headless}"/>
@ -1662,22 +1567,6 @@ some of its classes - at present JMeter can only run from jar files.
</java>
</target>
<target name="_test_htmlparser16" depends="check-libs" if="htmlparser16.present">
<junit printsummary="on" fork="yes" dir="${basedir}/bin">
<sysproperty key="htmlParser.className" value="org.apache.jmeter.protocol.http.parser.HtmlParserHTMLParser16"/>
<classpath>
<fileset dir="${dest.jar}" includes="*.jar"/>
<pathelement location="${build.test}"/>
<path refid="classpath"/>
<fileset dir="${lib.opt}" includes="htmlparser1_6.jar"/>
<fileset dir="${lib.dir}" includes="htmlparserparser16.jar"/>
</classpath>
<formatter type="plain" usefile="no"/>
<test name="org.apache.jmeter.protocol.http.parser.TestHTMLParser16"/>
</junit>
<echo>Done!</echo>
</target>
<!--
In order to run JUnit, both junit.jar and optional.jar need to be on the Ant classpath
optional.jar is normally found in ANT_HOME/lib
@ -1712,30 +1601,5 @@ some of its classes - at present JMeter can only run from jar files.
<report format="frames" todir="reports"/>
</junitreport>
</target>
<!-- Run the HTML Parser unit tests -->
<target name="testparser" description="run the JUnit tests">
<echo message="**********************************"/>
<echo message="* Running HtmlParser unit tests *"/>
<echo message="**********************************"/>
<!--
<javac srcdir="${src}" includes="org/htmlparser/tests/**" debug="on">
<classpath>
<pathelement location="src"/>
<pathelement location="${junit.jar}"/>
<pathelement location="${commons-logging.jar}"/>
</classpath>
</javac>
-->
<java classname="org.htmlparser.tests.AllTests" fork="yes" failonerror="yes">
<classpath>
<pathelement location="${build.htmlparser}"/>
<pathelement location="${lib.dir}/htmlparser.jar"/>
<pathelement location="${junit.jar}"/>
<pathelement location="${log-kit.jar}"/>
</classpath>
<arg value="-text"/>
</java>
</target>
</project>

Binary file not shown.

Binary file not shown.

View File

@ -1,186 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.jmeter.protocol.http.parser;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import org.htmlparser.Node;
import org.htmlparser.Parser;
import org.htmlparser.Tag;
import org.htmlparser.tags.AppletTag;
import org.htmlparser.tags.BaseHrefTag;
import org.htmlparser.tags.BodyTag;
import org.htmlparser.tags.CompositeTag;
import org.htmlparser.tags.FrameTag;
import org.htmlparser.tags.ImageTag;
import org.htmlparser.tags.InputTag;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.tags.ScriptTag;
import org.htmlparser.util.NodeIterator;
import org.htmlparser.util.ParserException;
/**
* HtmlParser implementation using SourceForge's HtmlParser.
*
*/
class HtmlParserHTMLParser16 extends HTMLParser {
private static final Logger log = LoggingManager.getLoggerForClass();
static{
org.htmlparser.scanners.ScriptScanner.STRICT = false; // Try to ensure that more javascript code is processed OK ...
}
protected HtmlParserHTMLParser16() {
super();
log.info("Using htmlparser version 1.6");
}
protected boolean isReusable() {
return true;
}
/*
* (non-Javadoc)
*
* @see org.apache.jmeter.protocol.http.parser.HtmlParser#getEmbeddedResourceURLs(byte[],
* java.net.URL)
*/
public Iterator getEmbeddedResourceURLs(byte[] html, URL baseUrl, URLCollection urls) throws HTMLParseException {
if (log.isDebugEnabled()) log.debug("Parsing html of: " + baseUrl);
Parser htmlParser = null;
try {
String contents = new String(html);
htmlParser = new Parser();
htmlParser.setInputHTML(contents);
} catch (Exception e) {
throw new HTMLParseException(e);
}
// Now parse the DOM tree
try {
// we start to iterate through the elements
parseNodes(htmlParser.elements(), new URLPointer(baseUrl), urls);
log.debug("End : parseNodes");
} catch (ParserException e) {
throw new HTMLParseException(e);
}
return urls.iterator();
}
/*
* A dummy class to pass the pointer of URL.
*/
private static class URLPointer {
private URLPointer(URL newUrl) {
url = newUrl;
}
private URL url;
}
/**
* Recursively parse all nodes to pick up all URL s.
* @see e the nodes to be parsed
* @see baseUrl Base URL from which the HTML code was obtained
* @see urls URLCollection
*/
private void parseNodes(final NodeIterator e,
final URLPointer baseUrl, final URLCollection urls)
throws HTMLParseException, ParserException {
while(e.hasMoreNodes()) {
Node node = e.nextNode();
// a url is always in a Tag.
if (!(node instanceof Tag)) {
continue;
}
Tag tag = (Tag) node;
String tagname=tag.getTagName();
String binUrlStr = null;
// first we check to see if body tag has a
// background set
if (tag instanceof BodyTag) {
binUrlStr = tag.getAttribute(ATT_BACKGROUND);
} else if (tag instanceof BaseHrefTag) {
BaseHrefTag baseHref = (BaseHrefTag) tag;
String baseref = baseHref.getBaseUrl().toString();
try {
if (!baseref.equals(""))// Bugzilla 30713
{
baseUrl.url = new URL(baseUrl.url, baseHref.getBaseUrl());
}
} catch (MalformedURLException e1) {
throw new HTMLParseException(e1);
}
} else if (tag instanceof ImageTag) {
ImageTag image = (ImageTag) tag;
binUrlStr = image.getImageURL();
} else if (tag instanceof AppletTag) {
// look for applets
// This will only work with an Applet .class file.
// Ideally, this should be upgraded to work with Objects (IE)
// and archives (.jar and .zip) files as well.
AppletTag applet = (AppletTag) tag;
binUrlStr = applet.getAppletClass();
} else if (tag instanceof InputTag) {
// we check the input tag type for image
if (ATT_IS_IMAGE.equalsIgnoreCase(tag.getAttribute(ATT_TYPE))) {
// then we need to download the binary
binUrlStr = tag.getAttribute(ATT_SRC);
}
} else if (tag instanceof LinkTag) {
LinkTag link = (LinkTag) tag;
if (link.getChild(0) instanceof ImageTag) {
ImageTag img = (ImageTag) link.getChild(0);
binUrlStr = img.getImageURL();
}
} else if (tag instanceof ScriptTag) {
binUrlStr = tag.getAttribute(ATT_SRC);
} else if (tag instanceof FrameTag) {
binUrlStr = tag.getAttribute(ATT_SRC);
} else if (tagname.equalsIgnoreCase(TAG_EMBED)
|| tagname.equalsIgnoreCase(TAG_BGSOUND)){
binUrlStr = tag.getAttribute(ATT_SRC);
} else if (tagname.equalsIgnoreCase(TAG_LINK)) {
// Putting the string first means it works even if the attribute is null
if (STYLESHEET.equalsIgnoreCase(tag.getAttribute(ATT_REL))) {
binUrlStr = tag.getAttribute(ATT_HREF);
}
} else {
binUrlStr = tag.getAttribute(ATT_BACKGROUND);
}
if (binUrlStr != null) {
urls.addURL(binUrlStr, baseUrl.url);
}
// second, if the tag was a composite tag,
// recursively parse its children.
if (tag instanceof CompositeTag) {
CompositeTag composite = (CompositeTag) tag;
parseNodes(composite.elements(), baseUrl, urls);
}
}
}
}

View File

@ -18,7 +18,6 @@
package org.apache.jmeter.protocol.http.parser;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Iterator;
@ -27,44 +26,33 @@ import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import org.htmlparser.Node;
import org.htmlparser.NodeReader;
import org.htmlparser.Parser;
import org.htmlparser.scanners.AppletScanner;
import org.htmlparser.scanners.BaseHrefScanner;
import org.htmlparser.scanners.BgSoundScanner;
import org.htmlparser.scanners.BodyScanner;
import org.htmlparser.scanners.FrameScanner;
import org.htmlparser.scanners.InputTagScanner;
import org.htmlparser.scanners.LinkScanner;
import org.htmlparser.scanners.LinkTagScanner;
import org.htmlparser.scanners.ScriptScanner;
import org.htmlparser.Tag;
import org.htmlparser.tags.AppletTag;
import org.htmlparser.tags.BaseHrefTag;
import org.htmlparser.tags.BgSoundTag;
import org.htmlparser.tags.BodyTag;
import org.htmlparser.tags.CompositeTag;
import org.htmlparser.tags.FrameTag;
import org.htmlparser.tags.ImageTag;
import org.htmlparser.tags.InputTag;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.tags.LinkTagTag;
import org.htmlparser.tags.ScriptTag;
import org.htmlparser.tags.Tag;
import org.htmlparser.util.DefaultParserFeedback;
import org.htmlparser.util.NodeIterator;
import org.htmlparser.util.ParserException;
/**
* HtmlParser implementation using SourceForge's HtmlParser.
*
* @version $Revision$ updated on $Date$
*/
class HtmlParserHTMLParser extends HTMLParser {
/** Used to store the Logger (used for debug and error messages). */
private static final Logger log = LoggingManager.getLoggerForClass();
public class HtmlParserHTMLParser extends HTMLParser {
private static final Logger log = LoggingManager.getLoggerForClass();
protected HtmlParserHTMLParser() {
static{
org.htmlparser.scanners.ScriptScanner.STRICT = false; // Try to ensure that more javascript code is processed OK ...
}
protected HtmlParserHTMLParser() {
super();
log.info("Using htmlparser implementation provided with JMeter");
log.info("Using htmlparser version 2.0");
}
protected boolean isReusable() {
@ -84,108 +72,16 @@ class HtmlParserHTMLParser extends HTMLParser {
Parser htmlParser = null;
try {
String contents = new String(html);
StringReader reader = new StringReader(contents);
NodeReader nreader = new NodeReader(reader, contents.length());
htmlParser = new Parser(nreader, new DefaultParserFeedback());
addTagListeners(htmlParser);
htmlParser = new Parser();
htmlParser.setInputHTML(contents);
} catch (Exception e) {
throw new HTMLParseException(e);
}
// Now parse the DOM tree
// look for applets
// This will only work with an Applet .class file.
// Ideally, this should be upgraded to work with Objects (IE)
// and archives (.jar and .zip) files as well.
try {
// we start to iterate through the elements
for (NodeIterator e = htmlParser.elements(); e.hasMoreNodes();) {
Node node = e.nextNode();
String binUrlStr = null;
// first we check to see if body tag has a
// background set and we set the NodeIterator
// to the child elements inside the body
if (node instanceof BodyTag) {
BodyTag body = (BodyTag) node;
binUrlStr = body.getAttribute(ATT_BACKGROUND);
// if the body tag exists, we get the elements
// within the body tag. if we don't we won't
// see the body of the page. The only catch
// with this is if there are images after the
// closing body tag, it won't get parsed. If
// someone puts it outside the body tag, it
// is probably a mistake. Plus it's bad to
// have important content after the closing
// body tag. Peter Lin 10-9-03
e = body.elements();
} else if (node instanceof BaseHrefTag) {
BaseHrefTag baseHref = (BaseHrefTag) node;
String baseref = baseHref.getBaseUrl();
try {
if (!baseref.equals(""))// Bugzilla 30713 // $NON-NLS-1$
{
baseUrl = new URL(baseUrl, baseref);
}
} catch (MalformedURLException e1) {
throw new HTMLParseException(e1);
}
} else if (node instanceof ImageTag) {
ImageTag image = (ImageTag) node;
binUrlStr = image.getImageURL();
} else if (node instanceof AppletTag) {
AppletTag applet = (AppletTag) node;
binUrlStr = applet.getAppletClass();
} else if (node instanceof InputTag) {
InputTag input = (InputTag) node;
// we check the input tag type for image
String strType = input.getAttribute(ATT_TYPE);
if (ATT_IS_IMAGE.equalsIgnoreCase(strType)) {
// then we need to download the binary
binUrlStr = input.getAttribute(ATT_SRC);
}
} else if (node instanceof LinkTag) {
LinkTag link = (LinkTag) node;
if (link.getChild(0) instanceof ImageTag) {
ImageTag img = (ImageTag) link.getChild(0);
binUrlStr = img.getImageURL();
}
} else if (node instanceof ScriptTag) {
ScriptTag script = (ScriptTag) node;
binUrlStr = script.getAttribute(ATT_SRC);
} else if (node instanceof FrameTag) {
FrameTag tag = (FrameTag) node;
binUrlStr = tag.getAttribute(ATT_SRC);
} else if (node instanceof LinkTagTag) {
LinkTagTag script = (LinkTagTag) node;
if (STYLESHEET.equalsIgnoreCase(script.getAttribute(ATT_REL))) {
binUrlStr = script.getAttribute(ATT_HREF);
}
} else if (node instanceof FrameTag) {
FrameTag script = (FrameTag) node;
binUrlStr = script.getAttribute(ATT_SRC);
} else if (node instanceof BgSoundTag) {
BgSoundTag script = (BgSoundTag) node;
binUrlStr = script.getAttribute(ATT_SRC);
} else if (node instanceof Tag) {
Tag tag = (Tag) node;
String tagname=tag.getTagName();
if (tagname.equalsIgnoreCase(TAG_EMBED)){
binUrlStr = tag.getAttribute(ATT_SRC);
} else {
binUrlStr = tag.getAttribute(ATT_BACKGROUND);
}
}
if (binUrlStr == null) {
continue;
}
urls.addURL(binUrlStr, baseUrl);
}
parseNodes(htmlParser.elements(), new URLPointer(baseUrl), urls);
log.debug("End : parseNodes");
} catch (ParserException e) {
throw new HTMLParseException(e);
@ -193,36 +89,99 @@ class HtmlParserHTMLParser extends HTMLParser {
return urls.iterator();
}
/**
* Returns a node representing a whole xml given an xml document.
*
* @param text
* an xml document
* @return a node representing a whole xml
*
* @throws SAXException
* indicates an error parsing the xml document
/*
* A dummy class to pass the pointer of URL.
*/
private static void addTagListeners(Parser parser) {
log.debug("Start : addTagListeners");
// add body tag scanner
parser.addScanner(new BodyScanner());
// add BaseHRefTag scanner
parser.addScanner(new BaseHrefScanner());
// add ImageTag and BaseHrefTag scanners
LinkScanner linkScanner = new LinkScanner(LinkTag.LINK_TAG_FILTER);
// parser.addScanner(linkScanner);
parser.addScanner(linkScanner.createImageScanner(ImageTag.IMAGE_TAG_FILTER));
parser.addScanner(linkScanner.createBaseHREFScanner("-b")); // $NON-NLS-1$
// Taken from org.htmlparser.Parser
// add input tag scanner
parser.addScanner(new InputTagScanner());
// add applet tag scanner
parser.addScanner(new AppletScanner());
parser.addScanner(new ScriptScanner());
parser.addScanner(new LinkTagScanner());
parser.addScanner(new FrameScanner());
parser.addScanner(new BgSoundScanner());
}
private static class URLPointer {
private URLPointer(URL newUrl) {
url = newUrl;
}
private URL url;
}
/**
* Recursively parse all nodes to pick up all URL s.
* @see e the nodes to be parsed
* @see baseUrl Base URL from which the HTML code was obtained
* @see urls URLCollection
*/
private void parseNodes(final NodeIterator e,
final URLPointer baseUrl, final URLCollection urls)
throws HTMLParseException, ParserException {
while(e.hasMoreNodes()) {
Node node = e.nextNode();
// a url is always in a Tag.
if (!(node instanceof Tag)) {
continue;
}
Tag tag = (Tag) node;
String tagname=tag.getTagName();
String binUrlStr = null;
// first we check to see if body tag has a
// background set
if (tag instanceof BodyTag) {
binUrlStr = tag.getAttribute(ATT_BACKGROUND);
} else if (tag instanceof BaseHrefTag) {
BaseHrefTag baseHref = (BaseHrefTag) tag;
String baseref = baseHref.getBaseUrl().toString();
try {
if (!baseref.equals(""))// Bugzilla 30713
{
baseUrl.url = new URL(baseUrl.url, baseHref.getBaseUrl());
}
} catch (MalformedURLException e1) {
throw new HTMLParseException(e1);
}
} else if (tag instanceof ImageTag) {
ImageTag image = (ImageTag) tag;
binUrlStr = image.getImageURL();
} else if (tag instanceof AppletTag) {
// look for applets
// This will only work with an Applet .class file.
// Ideally, this should be upgraded to work with Objects (IE)
// and archives (.jar and .zip) files as well.
AppletTag applet = (AppletTag) tag;
binUrlStr = applet.getAppletClass();
} else if (tag instanceof InputTag) {
// we check the input tag type for image
if (ATT_IS_IMAGE.equalsIgnoreCase(tag.getAttribute(ATT_TYPE))) {
// then we need to download the binary
binUrlStr = tag.getAttribute(ATT_SRC);
}
} else if (tag instanceof LinkTag) {
LinkTag link = (LinkTag) tag;
if (link.getChild(0) instanceof ImageTag) {
ImageTag img = (ImageTag) link.getChild(0);
binUrlStr = img.getImageURL();
}
} else if (tag instanceof ScriptTag) {
binUrlStr = tag.getAttribute(ATT_SRC);
} else if (tag instanceof FrameTag) {
binUrlStr = tag.getAttribute(ATT_SRC);
} else if (tagname.equalsIgnoreCase(TAG_EMBED)
|| tagname.equalsIgnoreCase(TAG_BGSOUND)){
binUrlStr = tag.getAttribute(ATT_SRC);
} else if (tagname.equalsIgnoreCase(TAG_LINK)) {
// Putting the string first means it works even if the attribute is null
if (STYLESHEET.equalsIgnoreCase(tag.getAttribute(ATT_REL))) {
binUrlStr = tag.getAttribute(ATT_HREF);
}
} else {
binUrlStr = tag.getAttribute(ATT_BACKGROUND);
}
if (binUrlStr != null) {
urls.addURL(binUrlStr, baseUrl.url);
}
// second, if the tag was a composite tag,
// recursively parse its children.
if (tag instanceof CompositeTag) {
CompositeTag composite = (CompositeTag) tag;
parseNodes(composite.elements(), baseUrl, urls);
}
}
}
}

View File

@ -1,212 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.apache.jmeter.protocol.http.parser;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import java.util.Vector;
import org.apache.jmeter.junit.JMeterTestCase;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import junit.framework.TestSuite;
public class TestHTMLParser16 extends JMeterTestCase {
private static final Logger log = LoggingManager.getLoggerForClass();
public TestHTMLParser16(String arg0) {
super(arg0);
}
private String parserName;
private int testNumber = 0;
public TestHTMLParser16(String name, int test) {
super(name);
testNumber = test;
}
public TestHTMLParser16(String name, String parser, int test) {
super(name);
testNumber = test;
parserName = parser;
}
private static class TestData {
private String fileName;
private String baseURL;
private String expectedSet;
private String expectedList;
private TestData(String f, String b, String s, String l) {
fileName = f;
baseURL = b;
expectedSet = s;
expectedList = l;
}
// private TestData(String f, String b, String s) {
// this(f, b, s, null);
// }
}
// List of parsers to test. Should probably be derived automatically
private static final String[] PARSERS = {
"org.apache.jmeter.protocol.http.parser.HtmlParserHTMLParser16",
};
private static final TestData[] TESTS = new TestData[] {
new TestData("testfiles/HTMLParserTestCase.html",
"http://localhost/mydir/myfile.html",
"testfiles/HTMLParserTestCase.set",
"testfiles/HTMLParserTestCase.all"),
new TestData("testfiles/HTMLParserTestCaseWithBaseHRef.html",
"http://localhost/mydir/myfile.html",
"testfiles/HTMLParserTestCaseBase.set",
"testfiles/HTMLParserTestCaseBase.all"),
new TestData("testfiles/HTMLParserTestCaseWithBaseHRef2.html",
"http://localhost/mydir/myfile.html",
"testfiles/HTMLParserTestCaseBase.set",
"testfiles/HTMLParserTestCaseBase.all"),
new TestData("testfiles/HTMLParserTestCaseWithMissingBaseHRef.html",
"http://localhost/mydir/images/myfile.html",
"testfiles/HTMLParserTestCaseBase.set",
"testfiles/HTMLParserTestCaseBase.all"),
new TestData("testfiles/HTMLParserTestCase2.html",
"http:", "", ""), // Dummy as the file has no entries
new TestData("testfiles/HTMLParserTestCase3.html",
"http:", "", ""), // Dummy as the file has no entries
new TestData("testfiles/HTMLParserTestCaseWithComments.html",
"http://localhost/mydir/myfile.html",
"testfiles/HTMLParserTestCaseBase.set",
"testfiles/HTMLParserTestCaseBase.all"),
new TestData("testfiles/HTMLScript.html",
"http://localhost/",
"testfiles/HTMLScript.set",
"testfiles/HTMLScript.all"),
new TestData("testfiles/HTMLParserTestFrames.html",
"http://localhost/",
"testfiles/HTMLParserTestFrames.all",
"testfiles/HTMLParserTestFrames.all"),
};
public static junit.framework.Test suite() {
TestSuite suite = new TestSuite("TestHTMLParser");
for (int i = 0; i < PARSERS.length; i++) {
TestSuite ps = new TestSuite(PARSERS[i]);// Identify subtests
for (int j = 0; j < TESTS.length; j++) {
TestSuite ts = new TestSuite(TESTS[j].fileName);
ts.addTest(new TestHTMLParser16("testParserSet", PARSERS[i], j));
ts.addTest(new TestHTMLParser16("testParserList", PARSERS[i], j));
ps.addTest(ts);
}
suite.addTest(ps);
}
return suite;
}
public void testParserSet() throws Exception {
HTMLParser p = HTMLParser.getParser(parserName);
filetest(p, TESTS[testNumber].fileName, TESTS[testNumber].baseURL, TESTS[testNumber].expectedSet, null,
false);
}
public void testParserList() throws Exception {
HTMLParser p = HTMLParser.getParser(parserName);
filetest(p, TESTS[testNumber].fileName, TESTS[testNumber].baseURL, TESTS[testNumber].expectedList,
new Vector(), true);
}
private static void filetest(HTMLParser p, String file, String url, String resultFile, Collection c,
boolean orderMatters) // Does the order matter?
throws Exception {
String parserName = p.getClass().getName().substring("org.apache.jmeter.protocol.http.parser.".length());
String fname = file.substring(file.indexOf("/")+1);
log.debug("file " + file);
File f = findTestFile(file);
byte[] buffer = new byte[(int) f.length()];
int len = new FileInputStream(f).read(buffer);
assertEquals(len, buffer.length);
Iterator result;
if (c == null) {
result = p.getEmbeddedResourceURLs(buffer, new URL(url));
} else {
result = p.getEmbeddedResourceURLs(buffer, new URL(url), c);
}
/*
* TODO: Exact ordering is only required for some tests; change the
* comparison to do a set compare where necessary.
*/
Iterator expected;
if (orderMatters) {
expected = getFile(resultFile).iterator();
} else {
// Convert both to Sets
expected = new TreeSet(getFile(resultFile)).iterator();
TreeSet temp = new TreeSet(new Comparator() {
public int compare(Object o1, Object o2) {
return (o1.toString().compareTo(o2.toString()));
}
});
while (result.hasNext()) {
temp.add(result.next());
}
result = temp.iterator();
}
while (expected.hasNext()) {
Object next = expected.next();
assertTrue(fname+"::"+parserName + "::Expecting another result " + next, result.hasNext());
try {
assertEquals(fname+"::"+parserName + "(next)", next, ((URL) result.next()).toString());
} catch (ClassCastException e) {
fail(fname+"::"+parserName + "::Expected URL, but got " + e.toString());
}
}
assertFalse(fname+"::"+parserName + "::Should have reached the end of the results", result.hasNext());
}
// Get expected results as a List
private static List getFile(String file) throws Exception {
ArrayList al = new ArrayList();
if (file != null && file.length() > 0) {
BufferedReader br = new BufferedReader(new FileReader(findTestFile(file)));
String line = br.readLine();
while (line != null) {
al.add(line);
line = br.readLine();
}
br.close();
}
return al;
}
}

View File

@ -64,6 +64,7 @@ has been removed.
<li>Bug 33964 - send file as entire post body if name/type are omitted</li>
<li>HTTP Mirror Server Workbench element</li>
<li>Bug 41253 - extend XPathExtractor to work with non-NodeList XPath expressions</li>
<li>Change to htmlparser 2.0</li>
</ul>
<h4>Bug fixes:</h4>