Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: #2870 SOAP request with attachments #6218

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 136 additions & 19 deletions bin/templates/build-webservice-test-plan.jmx
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="2.5" jmeter="2.10-SNAPSHOT.20130803">
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.6.2">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="Test Plan" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
<boolProp name="TestPlan.tearDown_on_shutdown">false</boolProp>
</TestPlan>
<hashTree>
<Arguments guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
Expand All @@ -27,20 +26,13 @@
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="HTTPSampler.domain">${host}</stringProp>
<stringProp name="HTTPSampler.port"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path"></stringProp>
<stringProp name="HTTPSampler.concurrentPool">4</stringProp>
</ConfigTestElement>
<hashTree/>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Number of Users" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<boolProp name="LoopController.continue_forever">false</boolProp>
<stringProp name="LoopController.loops">2</stringProp>
<boolProp name="LoopController.continue_forever">false</boolProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">5</stringProp>
<stringProp name="ThreadGroup.ramp_time">5</stringProp>
Expand All @@ -49,6 +41,8 @@
<boolProp name="ThreadGroup.scheduler">false</boolProp>
<stringProp name="ThreadGroup.duration"></stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
<boolProp name="ThreadGroup.delayedStart">false</boolProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
</ThreadGroup>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="Soap Request" enabled="true">
Expand All @@ -68,20 +62,18 @@
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.domain"></stringProp>
<stringProp name="HTTPSampler.port"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path">/WeatherWS/Weather.asmx</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<boolProp name="HTTPSampler.monitor">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<boolProp name="HTTPSampler.BROWSER_COMPATIBLE_MULTIPART">false</boolProp>
<boolProp name="HTTPSampler.image_parser">false</boolProp>
<boolProp name="HTTPSampler.concurrentDwn">false</boolProp>
<stringProp name="HTTPSampler.concurrentPool">6</stringProp>
<boolProp name="HTTPSampler.md5">false</boolProp>
<intProp name="HTTPSampler.ipSourceType">0</intProp>
</HTTPSamplerProxy>
<hashTree>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
Expand All @@ -105,6 +97,7 @@
<stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
<boolProp name="Assertion.assume_success">false</boolProp>
<intProp name="Assertion.test_type">16</intProp>
<stringProp name="Assertion.custom_message"></stringProp>
</ResponseAssertion>
<hashTree/>
</hashTree>
Expand Down Expand Up @@ -179,6 +172,130 @@
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="SwA - Thread Group" enabled="true">
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
<stringProp name="LoopController.loops">1</stringProp>
<boolProp name="LoopController.continue_forever">false</boolProp>
</elementProp>
<stringProp name="ThreadGroup.num_threads">1</stringProp>
<stringProp name="ThreadGroup.ramp_time">1</stringProp>
<boolProp name="ThreadGroup.delayedStart">false</boolProp>
<boolProp name="ThreadGroup.scheduler">false</boolProp>
<stringProp name="ThreadGroup.duration"></stringProp>
<stringProp name="ThreadGroup.delay"></stringProp>
<boolProp name="ThreadGroup.same_user_on_next_iteration">true</boolProp>
</ThreadGroup>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="SOAP Request with attachments" enabled="true">
<elementProp name="HTTPsampler.Files" elementType="HTTPFileArgs">
<collectionProp name="HTTPFileArgs.files">
<elementProp name="logo.svg" elementType="HTTPFileArg">
<stringProp name="File.mimetype">image/svg+xml</stringProp>
<stringProp name="File.path">logo.svg</stringProp>
<stringProp name="File.paramname">file</stringProp>
</elementProp>
<elementProp name="README.md" elementType="HTTPFileArg">
<stringProp name="File.mimetype">text/html</stringProp>
<stringProp name="File.path">README.md</stringProp>
<stringProp name="File.paramname">file2</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<boolProp name="HTTPSampler.postBodyRaw">false</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments">
<elementProp name="soap" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.metadata">=</stringProp>
<boolProp name="HTTPArgument.use_equals">true</boolProp>
<stringProp name="HTTPArgument.content_type">text/xml</stringProp>
<stringProp name="Argument.name">soap</stringProp>
<stringProp name="Argument.value">&lt;?xml version=&quot;1.0&quot;?&gt; &lt;soap:Envelope xmlns:soap=&quot;http://schemas.xmlsoap.org/soap/envelope/&quot; xmlns:ss=&quot;http://WebUpload/UploadService&quot;&gt; &lt;soap:Header&gt; &lt;ss:AuthHeader xmlns:ss=&quot;http://WebUpload/UploadService&quot;&gt; &lt;ss:Username&gt;joep&lt;/ss:Username&gt; &lt;ss:Password&gt;sdsfece&lt;/ss:Password&gt; &lt;/ss:AuthHeader&gt; &lt;/soap:Header&gt; &lt;soap:Body&gt; &lt;ss:StoreDocument xmlns:ss=&quot;http://WebUpload/UploadService&quot;&gt; &lt;ss:metadata&gt; &lt;ss:MetadataEntry&gt; &lt;ss:Key&gt;TemplateID&lt;/ss:Key&gt; &lt;ss:Value&gt;${templateId}&lt;/ss:Value&gt; &lt;/ss:MetadataEntry&gt; &lt;ss:MetadataEntry&gt; &lt;ss:Key&gt;Project&lt;/ss:Key&gt; &lt;ss:Value&gt;KarlI&lt;/ss:Value&gt; &lt;/ss:MetadataEntry&gt; &lt;/ss:metadata&gt; &lt;/ss:StoreDocument&gt; &lt;/soap:Body&gt; &lt;/soap:Envelope&gt;</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.domain">localhost</stringProp>
<stringProp name="HTTPSampler.port">8080</stringProp>
<stringProp name="HTTPSampler.protocol">https</stringProp>
<stringProp name="HTTPSampler.path">/UploadService.asmx</stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.follow_redirects">true</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<boolProp name="HTTPSampler.BROWSER_COMPATIBLE_MULTIPART">false</boolProp>
<boolProp name="HTTPSampler.image_parser">false</boolProp>
<boolProp name="HTTPSampler.concurrentDwn">false</boolProp>
<stringProp name="HTTPSampler.concurrentPool">6</stringProp>
<boolProp name="HTTPSampler.md5">false</boolProp>
<intProp name="HTTPSampler.ipSourceType">0</intProp>
<stringProp name="TestPlan.comments">Add SOAP message into Parameters section with proper content type (variables can be used there). Add files into Files Upload section.</stringProp>
</HTTPSamplerProxy>
<hashTree>
<HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
<collectionProp name="HeaderManager.headers">
<elementProp name="" elementType="Header">
<stringProp name="Header.name">SOAPAction</stringProp>
<stringProp name="Header.value">&quot;http://WebUpload/UploadService#StoreDocument&quot;</stringProp>
</elementProp>
<elementProp name="" elementType="Header">
<stringProp name="Header.name">Content-Type</stringProp>
<stringProp name="Header.value">multipart/related</stringProp>
</elementProp>
</collectionProp>
<stringProp name="TestPlan.comments">The most important step is to change request content type to: multipart/related</stringProp>
</HeaderManager>
<hashTree/>
<Arguments guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments">
<elementProp name="templateId" elementType="Argument">
<stringProp name="Argument.name">templateId</stringProp>
<stringProp name="Argument.value">45</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</Arguments>
<hashTree/>
</hashTree>
<ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="true">
<boolProp name="ResultCollector.error_logging">false</boolProp>
<objProp>
<name>saveConfig</name>
<value class="SampleSaveConfiguration">
<time>true</time>
<latency>true</latency>
<timestamp>true</timestamp>
<success>true</success>
<label>true</label>
<code>true</code>
<message>true</message>
<threadName>true</threadName>
<dataType>true</dataType>
<encoding>false</encoding>
<assertions>true</assertions>
<subresults>true</subresults>
<responseData>false</responseData>
<samplerData>false</samplerData>
<xml>false</xml>
<fieldNames>true</fieldNames>
<responseHeaders>false</responseHeaders>
<requestHeaders>false</requestHeaders>
<responseDataOnError>false</responseDataOnError>
<saveAssertionResultsFailureMessage>true</saveAssertionResultsFailureMessage>
<assertionsResultsToSave>0</assertionsResultsToSave>
<bytes>true</bytes>
<sentBytes>true</sentBytes>
<url>true</url>
<threadCounts>true</threadCounts>
<idleTime>true</idleTime>
<connectTime>true</connectTime>
</value>
</objProp>
<stringProp name="filename"></stringProp>
</ResultCollector>
<hashTree/>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@
*/
public class HTTPHC4Impl extends HTTPHCAbstractImpl {

private static final String MULTIPART_PREFIX = "multipart/";
private static final String CONTEXT_ATTRIBUTE_AUTH_MANAGER = "__jmeter.A_M__";

private static final String JMETER_VARIABLE_USER_TOKEN = "__jmeter.U_T__"; //$NON-NLS-1$
Expand Down Expand Up @@ -1546,10 +1547,22 @@ protected String setupHttpEntityEnclosingRequestData(HttpEntityEnclosingRequestB
// Check if we should do a multipart/form-data or an
// application/x-www-form-urlencoded post request
if(getUseMultipart()) {
if (entityEnclosingRequest.getHeaders(HTTPConstants.HEADER_CONTENT_TYPE).length > 0) {
log.info(
"Content-Header is set already on the request! Will be replaced by a Multipart-Header. Old headers: {}",
Arrays.asList(entityEnclosingRequest.getHeaders(HTTPConstants.HEADER_CONTENT_TYPE)));
String userDefinedMimeSubType = null;
final Header[] contentTypeHeaders = entityEnclosingRequest.getHeaders(HTTPConstants.HEADER_CONTENT_TYPE);
if (contentTypeHeaders.length > 0) {
if (StringUtils.isNotBlank(contentTypeHeaders[0].getValue()) && contentTypeHeaders[0].getValue().startsWith(MULTIPART_PREFIX)) {
final String mimeSubType = contentTypeHeaders[0].getValue().substring(MULTIPART_PREFIX.length());
if (StringUtils.isNotBlank(mimeSubType)) {
userDefinedMimeSubType = mimeSubType;
log.info("Content-Header subtype manually overriden! Will be set to: {}{}",
MULTIPART_PREFIX, userDefinedMimeSubType);
}
} else {
log.info(
"Content-Header is set already on the request! Will be replaced by a Multipart-Header. "
+ "Old headers: {}",
Arrays.asList(contentTypeHeaders));
}
entityEnclosingRequest.removeHeaders(HTTPConstants.HEADER_CONTENT_TYPE);
}

Expand All @@ -1562,6 +1575,9 @@ protected String setupHttpEntityEnclosingRequestData(HttpEntityEnclosingRequestB
}
// Write the request to our own stream
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
if (userDefinedMimeSubType != null) {
multipartEntityBuilder.setMimeSubtype(userDefinedMimeSubType);
}
multipartEntityBuilder.setCharset(charset);
if (doBrowserCompatibleMultipart) {
multipartEntityBuilder.setLaxMode();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,21 @@ void testParameterWithMimeTypeWithCharset() throws Exception {
Assertions.assertTrue(requestData.contains("charset=utf-8"));
}

@Test
void testParameterWithMultipartAndOverridenSubtype() throws Exception {
HTTPSamplerBase sampler = (HTTPSamplerBase) new HttpTestSampleGui().createTestElement();
sampler.setThreadContext(jmctx);
sampler.setDoMultipart(true);
sampler.setDoBrowserCompatibleMultipart(true);
HttpEntityEnclosingRequestBase post = new HttpPost();
post.addHeader(HTTPConstants.HEADER_CONTENT_TYPE, "multipart/related");
sampler.setHTTPFiles(new HTTPFileArg[] {new HTTPFileArg("filename", "file", "application/octect; charset=utf-8")});
HTTPHC4Impl hc = new HTTPHC4Impl(sampler);
String requestData = hc.setupHttpEntityEnclosingRequestData(post);
assertEquals(0, post.getHeaders(HTTPConstants.HEADER_CONTENT_TYPE).length);
Assertions.assertTrue(post.getEntity().getContentType().getValue().startsWith("multipart/related"));
Assertions.assertTrue(requestData.contains("charset=utf-8"));
}
@Test
void testParameterWithMultipartAndExplicitHeader() throws Exception {
HTTPSamplerBase sampler = (HTTPSamplerBase) new HttpTestSampleGui().createTestElement();
Expand Down
Loading