PageRenderTime 66ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/projects/jmeter-2.5.1/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 1081 lines | 762 code | 115 blank | 204 comment | 200 complexity | 2c05860c2eb7621f0fc768d89e9127bc MD5 | raw file
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package org.apache.jmeter.protocol.http.sampler;
  19. import java.io.ByteArrayOutputStream;
  20. import java.io.File;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.io.OutputStream;
  24. import java.net.InetAddress;
  25. import java.net.URI;
  26. import java.net.URL;
  27. import java.net.URLDecoder;
  28. import java.nio.charset.Charset;
  29. import java.security.GeneralSecurityException;
  30. import java.util.ArrayList;
  31. import java.util.HashMap;
  32. import java.util.List;
  33. import java.util.Map;
  34. import org.apache.http.Header;
  35. import org.apache.http.HttpConnection;
  36. import org.apache.http.HttpConnectionMetrics;
  37. import org.apache.http.HttpEntity;
  38. import org.apache.http.HttpException;
  39. import org.apache.http.HttpHost;
  40. import org.apache.http.HttpRequest;
  41. import org.apache.http.HttpResponse;
  42. import org.apache.http.HttpResponseInterceptor;
  43. import org.apache.http.NameValuePair;
  44. import org.apache.http.StatusLine;
  45. import org.apache.http.auth.AuthScope;
  46. import org.apache.http.auth.NTCredentials;
  47. import org.apache.http.auth.UsernamePasswordCredentials;
  48. import org.apache.http.client.CredentialsProvider;
  49. import org.apache.http.client.HttpClient;
  50. import org.apache.http.client.HttpRequestRetryHandler;
  51. import org.apache.http.client.entity.UrlEncodedFormEntity;
  52. import org.apache.http.client.methods.HttpDelete;
  53. import org.apache.http.client.methods.HttpGet;
  54. import org.apache.http.client.methods.HttpHead;
  55. import org.apache.http.client.methods.HttpOptions;
  56. import org.apache.http.client.methods.HttpPost;
  57. import org.apache.http.client.methods.HttpPut;
  58. import org.apache.http.client.methods.HttpRequestBase;
  59. import org.apache.http.client.methods.HttpTrace;
  60. import org.apache.http.client.methods.HttpUriRequest;
  61. import org.apache.http.client.params.ClientPNames;
  62. import org.apache.http.client.protocol.ResponseContentEncoding;
  63. import org.apache.http.conn.params.ConnRoutePNames;
  64. import org.apache.http.conn.scheme.Scheme;
  65. import org.apache.http.conn.scheme.SchemeRegistry;
  66. import org.apache.http.entity.FileEntity;
  67. import org.apache.http.entity.StringEntity;
  68. import org.apache.http.entity.mime.FormBodyPart;
  69. import org.apache.http.entity.mime.HttpMultipartMode;
  70. import org.apache.http.entity.mime.MultipartEntity;
  71. import org.apache.http.entity.mime.content.FileBody;
  72. import org.apache.http.entity.mime.content.StringBody;
  73. import org.apache.http.impl.client.AbstractHttpClient;
  74. import org.apache.http.impl.client.DefaultHttpClient;
  75. import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
  76. import org.apache.http.impl.client.RequestWrapper;
  77. import org.apache.http.message.BasicNameValuePair;
  78. import org.apache.http.params.BasicHttpParams;
  79. import org.apache.http.params.CoreConnectionPNames;
  80. import org.apache.http.params.CoreProtocolPNames;
  81. import org.apache.http.params.DefaultedHttpParams;
  82. import org.apache.http.params.HttpParams;
  83. import org.apache.http.protocol.BasicHttpContext;
  84. import org.apache.http.protocol.ExecutionContext;
  85. import org.apache.http.protocol.HttpContext;
  86. import org.apache.jmeter.protocol.http.control.AuthManager;
  87. import org.apache.jmeter.protocol.http.control.Authorization;
  88. import org.apache.jmeter.protocol.http.control.CacheManager;
  89. import org.apache.jmeter.protocol.http.control.CookieManager;
  90. import org.apache.jmeter.protocol.http.control.HeaderManager;
  91. import org.apache.jmeter.protocol.http.util.EncoderCache;
  92. import org.apache.jmeter.protocol.http.util.HC4TrustAllSSLSocketFactory;
  93. import org.apache.jmeter.protocol.http.util.HTTPArgument;
  94. import org.apache.jmeter.protocol.http.util.HTTPFileArg;
  95. import org.apache.jmeter.protocol.http.util.SlowHC4SSLSocketFactory;
  96. import org.apache.jmeter.protocol.http.util.SlowHC4SocketFactory;
  97. import org.apache.jmeter.testelement.property.CollectionProperty;
  98. import org.apache.jmeter.testelement.property.PropertyIterator;
  99. import org.apache.jmeter.util.JMeterUtils;
  100. import org.apache.jorphan.logging.LoggingManager;
  101. import org.apache.log.Logger;
  102. /**
  103. * HTTP Sampler using Apache HttpClient 4.x.
  104. *
  105. */
  106. public class HTTPHC4Impl extends HTTPHCAbstractImpl {
  107. private static final Logger log = LoggingManager.getLoggerForClass();
  108. /** retry count to be used (default 1); 0 = disable retries */
  109. private static final int RETRY_COUNT = JMeterUtils.getPropDefault("httpclient4.retrycount", 1);
  110. private static final String CONTEXT_METRICS = "jmeter_metrics"; // TODO hack, to be removed later
  111. private static final HttpResponseInterceptor METRICS_SAVER = new HttpResponseInterceptor(){
  112. public void process(HttpResponse response, HttpContext context)
  113. throws HttpException, IOException {
  114. HttpConnection conn = (HttpConnection) context.getAttribute(ExecutionContext.HTTP_CONNECTION);
  115. HttpConnectionMetrics metrics = conn.getMetrics();
  116. context.setAttribute(CONTEXT_METRICS, metrics);
  117. }
  118. };
  119. private static final ThreadLocal<Map<HttpClientKey, HttpClient>> HTTPCLIENTS =
  120. new ThreadLocal<Map<HttpClientKey, HttpClient>>(){
  121. @Override
  122. protected Map<HttpClientKey, HttpClient> initialValue() {
  123. return new HashMap<HttpClientKey, HttpClient>();
  124. }
  125. };
  126. // Scheme used for slow HTTP sockets. Cannot be set as a default, because must be set on an HttpClient instance.
  127. private static final Scheme SLOW_HTTP;
  128. // We always want to override the HTTPS scheme, because we want to trust all certificates and hosts
  129. private static final Scheme HTTPS_SCHEME;
  130. /*
  131. * Create a set of default parameters from the ones initially created.
  132. * This allows the defaults to be overridden if necessary from the properties file.
  133. */
  134. private static final HttpParams DEFAULT_HTTP_PARAMS;
  135. static {
  136. log.info("HTTP request retry count = "+RETRY_COUNT);
  137. // TODO use new setDefaultHttpParams(HttpParams params) static method when 4.1 is available
  138. final DefaultHttpClient dhc = new DefaultHttpClient();
  139. DEFAULT_HTTP_PARAMS = dhc.getParams(); // Get the default params
  140. dhc.getConnectionManager().shutdown(); // Tidy up
  141. // Process Apache HttpClient parameters file
  142. String file=JMeterUtils.getProperty("hc.parameters.file"); // $NON-NLS-1$
  143. if (file != null) {
  144. HttpClientDefaultParameters.load(file, DEFAULT_HTTP_PARAMS);
  145. }
  146. // Set up HTTP scheme override if necessary
  147. if (CPS_HTTP > 0) {
  148. log.info("Setting up HTTP SlowProtocol, cps="+CPS_HTTP);
  149. SLOW_HTTP = new Scheme(PROTOCOL_HTTP, DEFAULT_HTTP_PORT, new SlowHC4SocketFactory(CPS_HTTP));
  150. } else {
  151. SLOW_HTTP = null;
  152. }
  153. // We always want to override the HTTPS scheme
  154. Scheme https = null;
  155. if (CPS_HTTPS > 0) {
  156. log.info("Setting up HTTPS SlowProtocol, cps="+CPS_HTTPS);
  157. try {
  158. https = new Scheme(PROTOCOL_HTTPS, DEFAULT_HTTPS_PORT, new SlowHC4SSLSocketFactory(CPS_HTTPS));
  159. } catch (GeneralSecurityException e) {
  160. log.warn("Failed to initialise SLOW_HTTPS scheme, cps="+CPS_HTTPS, e);
  161. }
  162. } else {
  163. log.info("Setting up HTTPS TrustAll scheme");
  164. try {
  165. https = new Scheme(PROTOCOL_HTTPS, DEFAULT_HTTPS_PORT, new HC4TrustAllSSLSocketFactory());
  166. } catch (GeneralSecurityException e) {
  167. log.warn("Failed to initialise HTTPS TrustAll scheme", e);
  168. }
  169. }
  170. HTTPS_SCHEME = https;
  171. if (localAddress != null){
  172. DEFAULT_HTTP_PARAMS.setParameter(ConnRoutePNames.LOCAL_ADDRESS, localAddress);
  173. }
  174. }
  175. private volatile HttpUriRequest currentRequest; // Accessed from multiple threads
  176. protected HTTPHC4Impl(HTTPSamplerBase testElement) {
  177. super(testElement);
  178. }
  179. @Override
  180. protected HTTPSampleResult sample(URL url, String method,
  181. boolean areFollowingRedirect, int frameDepth) {
  182. HTTPSampleResult res = new HTTPSampleResult();
  183. res.setMonitor(isMonitor());
  184. res.setSampleLabel(url.toString()); // May be replaced later
  185. res.setHTTPMethod(method);
  186. res.setURL(url);
  187. HttpClient httpClient = setupClient(url);
  188. HttpRequestBase httpRequest = null;
  189. try {
  190. URI uri = url.toURI();
  191. if (method.equals(POST)) {
  192. httpRequest = new HttpPost(uri);
  193. } else if (method.equals(PUT)) {
  194. httpRequest = new HttpPut(uri);
  195. } else if (method.equals(HEAD)) {
  196. httpRequest = new HttpHead(uri);
  197. } else if (method.equals(TRACE)) {
  198. httpRequest = new HttpTrace(uri);
  199. } else if (method.equals(OPTIONS)) {
  200. httpRequest = new HttpOptions(uri);
  201. } else if (method.equals(DELETE)) {
  202. httpRequest = new HttpDelete(uri);
  203. } else if (method.equals(GET)) {
  204. httpRequest = new HttpGet(uri);
  205. } else {
  206. throw new IllegalArgumentException("Unexpected method: "+method);
  207. }
  208. setupRequest(url, httpRequest, res); // can throw IOException
  209. } catch (Exception e) {
  210. res.sampleStart();
  211. res.sampleEnd();
  212. HTTPSampleResult err = errorResult(e, res);
  213. err.setSampleLabel("Error: " + url.toString());
  214. return err;
  215. }
  216. HttpContext localContext = new BasicHttpContext();
  217. res.sampleStart();
  218. final CacheManager cacheManager = getCacheManager();
  219. if (cacheManager != null && GET.equalsIgnoreCase(method)) {
  220. if (cacheManager.inCache(url)) {
  221. res.sampleEnd();
  222. res.setResponseNoContent();
  223. res.setSuccessful(true);
  224. return res;
  225. }
  226. }
  227. try {
  228. currentRequest = httpRequest;
  229. // Handle the various methods
  230. if (method.equals(POST)) {
  231. String postBody = sendPostData((HttpPost)httpRequest);
  232. res.setQueryString(postBody);
  233. } else if (method.equals(PUT)) {
  234. String putBody = sendPutData((HttpPut)httpRequest);
  235. res.setQueryString(putBody);
  236. }
  237. HttpResponse httpResponse = httpClient.execute(httpRequest, localContext); // perform the sample
  238. // Needs to be done after execute to pick up all the headers
  239. res.setRequestHeaders(getConnectionHeaders((HttpRequest) localContext.getAttribute(ExecutionContext.HTTP_REQUEST)));
  240. Header contentType = httpResponse.getLastHeader(HEADER_CONTENT_TYPE);
  241. if (contentType != null){
  242. String ct = contentType.getValue();
  243. res.setContentType(ct);
  244. res.setEncodingAndType(ct);
  245. }
  246. HttpEntity entity = httpResponse.getEntity();
  247. if (entity != null) {
  248. InputStream instream = entity.getContent();
  249. res.setResponseData(readResponse(res, instream, (int) entity.getContentLength()));
  250. }
  251. res.sampleEnd(); // Done with the sampling proper.
  252. currentRequest = null;
  253. // Now collect the results into the HTTPSampleResult:
  254. StatusLine statusLine = httpResponse.getStatusLine();
  255. int statusCode = statusLine.getStatusCode();
  256. res.setResponseCode(Integer.toString(statusCode));
  257. res.setResponseMessage(statusLine.getReasonPhrase());
  258. res.setSuccessful(isSuccessCode(statusCode));
  259. res.setResponseHeaders(getResponseHeaders(httpResponse));
  260. if (res.isRedirect()) {
  261. final Header headerLocation = httpResponse.getLastHeader(HEADER_LOCATION);
  262. if (headerLocation == null) { // HTTP protocol violation, but avoids NPE
  263. throw new IllegalArgumentException("Missing location header");
  264. }
  265. res.setRedirectLocation(headerLocation.getValue());
  266. }
  267. // record some sizes to allow HTTPSampleResult.getBytes() with different options
  268. HttpConnectionMetrics metrics = (HttpConnectionMetrics) localContext.getAttribute(CONTEXT_METRICS);
  269. long headerBytes =
  270. res.getResponseHeaders().length() // condensed length (without \r)
  271. + httpResponse.getAllHeaders().length // Add \r for each header
  272. + 1 // Add \r for initial header
  273. + 2; // final \r\n before data
  274. long totalBytes = metrics.getReceivedBytesCount();
  275. res.setHeadersSize((int) headerBytes);
  276. res.setBodySize((int)(totalBytes - headerBytes));
  277. if (log.isDebugEnabled()) {
  278. log.debug("ResponseHeadersSize=" + res.getHeadersSize() + " Content-Length=" + res.getBodySize()
  279. + " Total=" + (res.getHeadersSize() + res.getBodySize()));
  280. }
  281. // If we redirected automatically, the URL may have changed
  282. if (getAutoRedirects()){
  283. HttpUriRequest req = (HttpUriRequest) localContext.getAttribute(ExecutionContext.HTTP_REQUEST);
  284. HttpHost target = (HttpHost) localContext.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
  285. URI redirectURI = req.getURI();
  286. if (redirectURI.isAbsolute()){
  287. res.setURL(redirectURI.toURL());
  288. } else {
  289. res.setURL(new URL(new URL(target.toURI()),redirectURI.toString()));
  290. }
  291. }
  292. // Store any cookies received in the cookie manager:
  293. saveConnectionCookies(httpResponse, res.getURL(), getCookieManager());
  294. // Save cache information
  295. if (cacheManager != null){
  296. cacheManager.saveDetails(httpResponse, res);
  297. }
  298. // Follow redirects and download page resources if appropriate:
  299. res = resultProcessing(areFollowingRedirect, frameDepth, res);
  300. } catch (IOException e) {
  301. res.sampleEnd();
  302. HTTPSampleResult err = errorResult(e, res);
  303. err.setSampleLabel("Error: " + url.toString());
  304. return err;
  305. } catch (RuntimeException e) {
  306. res.sampleEnd();
  307. HTTPSampleResult err = errorResult(e, res);
  308. err.setSampleLabel("Error: " + url.toString());
  309. return err;
  310. } finally {
  311. currentRequest = null;
  312. }
  313. return res;
  314. }
  315. /**
  316. * Holder class for all fields that define an HttpClient instance;
  317. * used as the key to the ThreadLocal map of HttpClient instances.
  318. */
  319. private static final class HttpClientKey {
  320. private final String target; // protocol://[user:pass@]host:[port]
  321. private final boolean hasProxy;
  322. private final String proxyHost;
  323. private final int proxyPort;
  324. private final String proxyUser;
  325. private final String proxyPass;
  326. private final int hashCode; // Always create hash because we will always need it
  327. public HttpClientKey(URL url, boolean b, String proxyHost,
  328. int proxyPort, String proxyUser, String proxyPass) {
  329. // N.B. need to separate protocol from authority otherwise http://server would match https://erver
  330. // could use separate fields, but simpler to combine them
  331. this.target = url.getProtocol()+"://"+url.getAuthority();
  332. this.hasProxy = b;
  333. this.proxyHost = proxyHost;
  334. this.proxyPort = proxyPort;
  335. this.proxyUser = proxyUser;
  336. this.proxyPass = proxyPass;
  337. this.hashCode = getHash();
  338. }
  339. private int getHash() {
  340. int hash = 17;
  341. hash = hash*31 + (hasProxy ? 1 : 0);
  342. if (hasProxy) {
  343. hash = hash*31 + getHash(proxyHost);
  344. hash = hash*31 + proxyPort;
  345. hash = hash*31 + getHash(proxyUser);
  346. hash = hash*31 + getHash(proxyPass);
  347. }
  348. hash = hash*31 + target.hashCode();
  349. return hash;
  350. }
  351. // Allow for null strings
  352. private int getHash(String s) {
  353. return s == null ? 0 : s.hashCode();
  354. }
  355. @Override
  356. public boolean equals (Object obj){
  357. if (this == obj) {
  358. return true;
  359. }
  360. if (!(obj instanceof HttpClientKey)) {
  361. return false;
  362. }
  363. HttpClientKey other = (HttpClientKey) obj;
  364. if (this.hasProxy) { // otherwise proxy String fields may be null
  365. return
  366. this.hasProxy == other.hasProxy &&
  367. this.proxyPort == other.proxyPort &&
  368. this.proxyHost.equals(other.proxyHost) &&
  369. this.proxyUser.equals(other.proxyUser) &&
  370. this.proxyPass.equals(other.proxyPass) &&
  371. this.target.equals(other.target);
  372. }
  373. // No proxy, so don't check proxy fields
  374. return
  375. this.hasProxy == other.hasProxy &&
  376. this.target.equals(other.target);
  377. }
  378. @Override
  379. public int hashCode(){
  380. return hashCode;
  381. }
  382. }
  383. private HttpClient setupClient(URL url) {
  384. Map<HttpClientKey, HttpClient> map = HTTPCLIENTS.get();
  385. final String host = url.getHost();
  386. final String proxyHost = getProxyHost();
  387. final int proxyPort = getProxyPortInt();
  388. boolean useStaticProxy = isStaticProxy(host);
  389. boolean useDynamicProxy = isDynamicProxy(proxyHost, proxyPort);
  390. // Lookup key - must agree with all the values used to create the HttpClient.
  391. HttpClientKey key = new HttpClientKey(url, (useStaticProxy || useDynamicProxy),
  392. useDynamicProxy ? proxyHost : PROXY_HOST,
  393. useDynamicProxy ? proxyPort : PROXY_PORT,
  394. useDynamicProxy ? getProxyUser() : PROXY_USER,
  395. useDynamicProxy ? getProxyPass() : PROXY_PASS);
  396. HttpClient httpClient = map.get(key);
  397. if (httpClient == null){ // One-time init for this client
  398. HttpParams clientParams = new DefaultedHttpParams(new BasicHttpParams(), DEFAULT_HTTP_PARAMS);
  399. httpClient = new DefaultHttpClient(clientParams){
  400. @Override
  401. protected HttpRequestRetryHandler createHttpRequestRetryHandler() {
  402. return new DefaultHttpRequestRetryHandler(RETRY_COUNT, false) {
  403. // TODO HACK to fix https://issues.apache.org/jira/browse/HTTPCLIENT-1120
  404. // can hopefully be removed when 4.1.3 or 4.2 are released
  405. @Override
  406. public boolean retryRequest(IOException ex, int count, HttpContext ctx) {
  407. Object request = ctx.getAttribute(ExecutionContext.HTTP_REQUEST);
  408. if(request instanceof HttpUriRequest){
  409. if (request instanceof RequestWrapper) {
  410. request = ((RequestWrapper) request).getOriginal();
  411. }
  412. if(((HttpUriRequest)request).isAborted()){
  413. log.warn("Workround for HTTPCLIENT-1120 request retry: "+ex);
  414. return false;
  415. }
  416. }
  417. /*
  418. * When connect fails due to abort, the request is not in the context.
  419. * Tried adding the request - with a new key - to the local context in the sample() method,
  420. * but the request was not flagged as aborted, so that did not help.
  421. * So we check for any specific exception that is triggered.
  422. */
  423. if (
  424. (ex instanceof java.net.BindException &&
  425. ex.getMessage().contains("Address already in use: connect"))
  426. ||
  427. ex.getMessage().contains("Request aborted") // plain IOException
  428. ) {
  429. /*
  430. * The above messages may be generated by aborted connects.
  431. * If either occurs in other situations, retrying is unlikely to help,
  432. * so preventing retry should not cause a problem.
  433. */
  434. log.warn("Workround for HTTPCLIENT-1120 connect retry: "+ex);
  435. return false;
  436. }
  437. return super.retryRequest(ex, count, ctx);
  438. } // end of hack
  439. }; // set retry count
  440. }
  441. };
  442. ((AbstractHttpClient) httpClient).addResponseInterceptor(new ResponseContentEncoding());
  443. ((AbstractHttpClient) httpClient).addResponseInterceptor(METRICS_SAVER); // HACK
  444. // Override the defualt schemes as necessary
  445. SchemeRegistry schemeRegistry = httpClient.getConnectionManager().getSchemeRegistry();
  446. if (SLOW_HTTP != null){
  447. schemeRegistry.register(SLOW_HTTP);
  448. }
  449. if (HTTPS_SCHEME != null){
  450. schemeRegistry.register(HTTPS_SCHEME);
  451. }
  452. // Set up proxy details
  453. if (useDynamicProxy){
  454. HttpHost proxy = new HttpHost(proxyHost, proxyPort);
  455. clientParams.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
  456. String proxyUser = getProxyUser();
  457. if (proxyUser.length() > 0) {
  458. ((AbstractHttpClient) httpClient).getCredentialsProvider().setCredentials(
  459. new AuthScope(proxyHost, proxyPort),
  460. new UsernamePasswordCredentials(proxyUser, getProxyPass()));
  461. }
  462. } else if (useStaticProxy) {
  463. HttpHost proxy = new HttpHost(PROXY_HOST, PROXY_PORT);
  464. clientParams.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
  465. if (PROXY_USER.length() > 0)
  466. ((AbstractHttpClient) httpClient).getCredentialsProvider().setCredentials(
  467. new AuthScope(PROXY_HOST, PROXY_PORT),
  468. new UsernamePasswordCredentials(PROXY_USER, PROXY_PASS));
  469. }
  470. if (log.isDebugEnabled()) {
  471. log.debug("Created new HttpClient: @"+System.identityHashCode(httpClient));
  472. }
  473. map.put(key, httpClient); // save the agent for next time round
  474. } else {
  475. if (log.isDebugEnabled()) {
  476. log.debug("Reusing the HttpClient: @"+System.identityHashCode(httpClient));
  477. }
  478. }
  479. // TODO - should this be done when the client is created?
  480. // If so, then the details need to be added as part of HttpClientKey
  481. setConnectionAuthorization(httpClient, url, getAuthManager());
  482. return httpClient;
  483. }
  484. private void setupRequest(URL url, HttpRequestBase httpRequest, HTTPSampleResult res)
  485. throws IOException {
  486. HttpParams requestParams = httpRequest.getParams();
  487. // Set up the local address if one exists
  488. final String ipSource = getIpSource();
  489. if (ipSource.length() > 0) {// Use special field ip source address (for pseudo 'ip spoofing')
  490. InetAddress inetAddr = InetAddress.getByName(ipSource);
  491. requestParams.setParameter(ConnRoutePNames.LOCAL_ADDRESS, inetAddr);
  492. } else if (localAddress != null){
  493. requestParams.setParameter(ConnRoutePNames.LOCAL_ADDRESS, localAddress);
  494. } else { // reset in case was set previously
  495. requestParams.removeParameter(ConnRoutePNames.LOCAL_ADDRESS);
  496. }
  497. int rto = getResponseTimeout();
  498. if (rto > 0){
  499. requestParams.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, rto);
  500. }
  501. int cto = getConnectTimeout();
  502. if (cto > 0){
  503. requestParams.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, cto);
  504. }
  505. requestParams.setBooleanParameter(ClientPNames.HANDLE_REDIRECTS, getAutoRedirects());
  506. // a well-behaved browser is supposed to send 'Connection: close'
  507. // with the last request to an HTTP server. Instead, most browsers
  508. // leave it to the server to close the connection after their
  509. // timeout period. Leave it to the JMeter user to decide.
  510. if (getUseKeepAlive()) {
  511. httpRequest.setHeader(HEADER_CONNECTION, KEEP_ALIVE);
  512. } else {
  513. httpRequest.setHeader(HEADER_CONNECTION, CONNECTION_CLOSE);
  514. }
  515. setConnectionHeaders(httpRequest, url, getHeaderManager(), getCacheManager());
  516. String cookies = setConnectionCookie(httpRequest, url, getCookieManager());
  517. if (res != null) {
  518. res.setCookies(cookies);
  519. }
  520. }
  521. /**
  522. * Set any default request headers to include
  523. *
  524. * @param request the HttpRequest to be used
  525. */
  526. protected void setDefaultRequestHeaders(HttpRequest request) {
  527. // Method left empty here, but allows subclasses to override
  528. }
  529. /**
  530. * Gets the ResponseHeaders
  531. *
  532. * @param response
  533. * containing the headers
  534. * @return string containing the headers, one per line
  535. */
  536. private String getResponseHeaders(HttpResponse response) {
  537. StringBuilder headerBuf = new StringBuilder();
  538. Header[] rh = response.getAllHeaders();
  539. headerBuf.append(response.getStatusLine());// header[0] is not the status line...
  540. headerBuf.append("\n"); // $NON-NLS-1$
  541. for (int i = 0; i < rh.length; i++) {
  542. headerBuf.append(rh[i].getName());
  543. headerBuf.append(": "); // $NON-NLS-1$
  544. headerBuf.append(rh[i].getValue());
  545. headerBuf.append("\n"); // $NON-NLS-1$
  546. }
  547. return headerBuf.toString();
  548. }
  549. /**
  550. * Extracts all the required cookies for that particular URL request and
  551. * sets them in the <code>HttpMethod</code> passed in.
  552. *
  553. * @param request <code>HttpRequest</code> for the request
  554. * @param url <code>URL</code> of the request
  555. * @param cookieManager the <code>CookieManager</code> containing all the cookies
  556. * @return a String containing the cookie details (for the response)
  557. * May be null
  558. */
  559. private String setConnectionCookie(HttpRequest request, URL url, CookieManager cookieManager) {
  560. String cookieHeader = null;
  561. if (cookieManager != null) {
  562. cookieHeader = cookieManager.getCookieHeaderForURL(url);
  563. if (cookieHeader != null) {
  564. request.setHeader(HEADER_COOKIE, cookieHeader);
  565. }
  566. }
  567. return cookieHeader;
  568. }
  569. /**
  570. * Extracts all the required non-cookie headers for that particular URL request and
  571. * sets them in the <code>HttpMethod</code> passed in
  572. *
  573. * @param request
  574. * <code>HttpRequest</code> which represents the request
  575. * @param url
  576. * <code>URL</code> of the URL request
  577. * @param headerManager
  578. * the <code>HeaderManager</code> containing all the cookies
  579. * for this <code>UrlConfig</code>
  580. * @param cacheManager the CacheManager (may be null)
  581. */
  582. private void setConnectionHeaders(HttpRequestBase request, URL url, HeaderManager headerManager, CacheManager cacheManager) {
  583. if (headerManager != null) {
  584. CollectionProperty headers = headerManager.getHeaders();
  585. if (headers != null) {
  586. PropertyIterator i = headers.iterator();
  587. while (i.hasNext()) {
  588. org.apache.jmeter.protocol.http.control.Header header
  589. = (org.apache.jmeter.protocol.http.control.Header)
  590. i.next().getObjectValue();
  591. String n = header.getName();
  592. // Don't allow override of Content-Length
  593. // TODO - what other headers are not allowed?
  594. if (! HEADER_CONTENT_LENGTH.equalsIgnoreCase(n)){
  595. String v = header.getValue();
  596. if (HEADER_HOST.equalsIgnoreCase(n)) {
  597. int port = url.getPort();
  598. v = v.replaceFirst(":\\d+$",""); // remove any port specification // $NON-NLS-1$ $NON-NLS-2$
  599. if (port != -1) {
  600. if (port == url.getDefaultPort()) {
  601. port = -1; // no need to specify the port if it is the default
  602. }
  603. }
  604. request.getParams().setParameter(ClientPNames.VIRTUAL_HOST, new HttpHost(v, port));
  605. } else {
  606. request.addHeader(n, v);
  607. }
  608. }
  609. }
  610. }
  611. }
  612. if (cacheManager != null){
  613. cacheManager.setHeaders(url, request);
  614. }
  615. }
  616. /**
  617. * Get all the request headers for the <code>HttpMethod</code>
  618. *
  619. * @param method
  620. * <code>HttpMethod</code> which represents the request
  621. * @return the headers as a string
  622. */
  623. private String getConnectionHeaders(HttpRequest method) {
  624. // Get all the request headers
  625. StringBuilder hdrs = new StringBuilder(100);
  626. Header[] requestHeaders = method.getAllHeaders();
  627. for(int i = 0; i < requestHeaders.length; i++) {
  628. // Exclude the COOKIE header, since cookie is reported separately in the sample
  629. if(!HEADER_COOKIE.equalsIgnoreCase(requestHeaders[i].getName())) {
  630. hdrs.append(requestHeaders[i].getName());
  631. hdrs.append(": "); // $NON-NLS-1$
  632. hdrs.append(requestHeaders[i].getValue());
  633. hdrs.append("\n"); // $NON-NLS-1$
  634. }
  635. }
  636. return hdrs.toString();
  637. }
  638. private void setConnectionAuthorization(HttpClient client, URL url, AuthManager authManager) {
  639. CredentialsProvider credentialsProvider =
  640. ((AbstractHttpClient) client).getCredentialsProvider();
  641. if (authManager != null) {
  642. Authorization auth = authManager.getAuthForURL(url);
  643. if (auth != null) {
  644. String username = auth.getUser();
  645. String realm = auth.getRealm();
  646. String domain = auth.getDomain();
  647. if (log.isDebugEnabled()){
  648. log.debug(username + " > D="+domain+" R="+realm);
  649. }
  650. credentialsProvider.setCredentials(
  651. new AuthScope(url.getHost(), url.getPort(), realm.length()==0 ? null : realm),
  652. new NTCredentials(username, auth.getPass(), localHost, domain));
  653. } else {
  654. credentialsProvider.clear();
  655. }
  656. } else {
  657. credentialsProvider.clear();
  658. }
  659. }
  660. // Helper class so we can generate request data without dumping entire file contents
  661. private static class ViewableFileBody extends FileBody {
  662. private boolean hideFileData;
  663. public ViewableFileBody(File file, String mimeType) {
  664. super(file, mimeType);
  665. hideFileData = false;
  666. }
  667. @Override
  668. public void writeTo(final OutputStream out) throws IOException {
  669. if (hideFileData) {
  670. out.write("<actual file content, not shown here>".getBytes());// encoding does not really matter here
  671. } else {
  672. super.writeTo(out);
  673. }
  674. }
  675. }
  676. // TODO needs cleaning up
  677. private String sendPostData(HttpPost post) throws IOException {
  678. // Buffer to hold the post body, except file content
  679. StringBuilder postedBody = new StringBuilder(1000);
  680. HTTPFileArg files[] = getHTTPFiles();
  681. // Check if we should do a multipart/form-data or an
  682. // application/x-www-form-urlencoded post request
  683. if(getUseMultipartForPost()) {
  684. // If a content encoding is specified, we use that as the
  685. // encoding of any parameter values
  686. String contentEncoding = getContentEncoding();
  687. if(contentEncoding != null && contentEncoding.length() == 0) {
  688. contentEncoding = null;
  689. }
  690. // Write the request to our own stream
  691. MultipartEntity multiPart = new MultipartEntity(
  692. getDoBrowserCompatibleMultipart() ? HttpMultipartMode.BROWSER_COMPATIBLE : HttpMultipartMode.STRICT);
  693. // Create the parts
  694. // Add any parameters
  695. PropertyIterator args = getArguments().iterator();
  696. while (args.hasNext()) {
  697. HTTPArgument arg = (HTTPArgument) args.next().getObjectValue();
  698. String parameterName = arg.getName();
  699. if (arg.isSkippable(parameterName)){
  700. continue;
  701. }
  702. FormBodyPart formPart;
  703. StringBody stringBody = new StringBody(arg.getValue(),
  704. Charset.forName(contentEncoding == null ? "US-ASCII" : contentEncoding));
  705. formPart = new FormBodyPart(arg.getName(), stringBody);
  706. multiPart.addPart(formPart);
  707. }
  708. // Add any files
  709. // Cannot retrieve parts once added to the MultiPartEntity, so have to save them here.
  710. ViewableFileBody[] fileBodies = new ViewableFileBody[files.length];
  711. for (int i=0; i < files.length; i++) {
  712. HTTPFileArg file = files[i];
  713. fileBodies[i] = new ViewableFileBody(new File(file.getPath()), file.getMimeType());
  714. multiPart.addPart(file.getParamName(),fileBodies[i]);
  715. }
  716. post.setEntity(multiPart);
  717. if (multiPart.isRepeatable()){
  718. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  719. for(ViewableFileBody fileBody : fileBodies){
  720. fileBody.hideFileData = true;
  721. }
  722. multiPart.writeTo(bos);
  723. for(ViewableFileBody fileBody : fileBodies){
  724. fileBody.hideFileData = false;
  725. }
  726. bos.flush();
  727. // We get the posted bytes using the encoding used to create it
  728. postedBody.append(new String(bos.toByteArray(),
  729. contentEncoding == null ? "US-ASCII" // $NON-NLS-1$ this is the default used by HttpClient
  730. : contentEncoding));
  731. bos.close();
  732. } else {
  733. postedBody.append("<Multipart was not repeatable, cannot view what was sent>"); // $NON-NLS-1$
  734. }
  735. // // Set the content type TODO - needed?
  736. // String multiPartContentType = multiPart.getContentType().getValue();
  737. // post.setHeader(HEADER_CONTENT_TYPE, multiPartContentType);
  738. } else { // not multipart
  739. // Check if the header manager had a content type header
  740. // This allows the user to specify his own content-type for a POST request
  741. Header contentTypeHeader = post.getFirstHeader(HEADER_CONTENT_TYPE);
  742. boolean hasContentTypeHeader = contentTypeHeader != null && contentTypeHeader.getValue() != null && contentTypeHeader.getValue().length() > 0;
  743. // If there are no arguments, we can send a file as the body of the request
  744. // TODO: needs a multiple file upload scenerio
  745. if(!hasArguments() && getSendFileAsPostBody()) {
  746. // If getSendFileAsPostBody returned true, it's sure that file is not null
  747. HTTPFileArg file = files[0];
  748. if(!hasContentTypeHeader) {
  749. // Allow the mimetype of the file to control the content type
  750. if(file.getMimeType() != null && file.getMimeType().length() > 0) {
  751. post.setHeader(HEADER_CONTENT_TYPE, file.getMimeType());
  752. }
  753. else {
  754. post.setHeader(HEADER_CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED);
  755. }
  756. }
  757. FileEntity fileRequestEntity = new FileEntity(new File(file.getPath()),(String) null);// TODO is null correct?
  758. post.setEntity(fileRequestEntity);
  759. // We just add placeholder text for file content
  760. postedBody.append("<actual file content, not shown here>");
  761. } else {
  762. // In a post request which is not multipart, we only support
  763. // parameters, no file upload is allowed
  764. // If a content encoding is specified, we set it as http parameter, so that
  765. // the post body will be encoded in the specified content encoding
  766. String contentEncoding = getContentEncoding();
  767. boolean haveContentEncoding = false;
  768. if(contentEncoding != null && contentEncoding.trim().length() > 0) {
  769. post.getParams().setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET, contentEncoding);
  770. haveContentEncoding = true;
  771. } else if (contentEncoding != null && contentEncoding.trim().length() == 0){
  772. contentEncoding=null;
  773. }
  774. // If none of the arguments have a name specified, we
  775. // just send all the values as the post body
  776. if(getSendParameterValuesAsPostBody()) {
  777. // Allow the mimetype of the file to control the content type
  778. // This is not obvious in GUI if you are not uploading any files,
  779. // but just sending the content of nameless parameters
  780. // TODO: needs a multiple file upload scenerio
  781. if(!hasContentTypeHeader) {
  782. HTTPFileArg file = files.length > 0? files[0] : null;
  783. if(file != null && file.getMimeType() != null && file.getMimeType().length() > 0) {
  784. post.setHeader(HEADER_CONTENT_TYPE, file.getMimeType());
  785. }
  786. else {
  787. // TODO - is this the correct default?
  788. post.setHeader(HEADER_CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED);
  789. }
  790. }
  791. // Just append all the parameter values, and use that as the post body
  792. StringBuilder postBody = new StringBuilder();
  793. PropertyIterator args = getArguments().iterator();
  794. while (args.hasNext()) {
  795. HTTPArgument arg = (HTTPArgument) args.next().getObjectValue();
  796. String value;
  797. if (haveContentEncoding){
  798. value = arg.getEncodedValue(contentEncoding);
  799. } else {
  800. value = arg.getEncodedValue();
  801. }
  802. postBody.append(value);
  803. }
  804. StringEntity requestEntity = new StringEntity(postBody.toString(), post.getFirstHeader(HEADER_CONTENT_TYPE).getValue(), contentEncoding);
  805. post.setEntity(requestEntity);
  806. postedBody.append(postBody.toString()); // TODO OK?
  807. } else {
  808. // It is a normal post request, with parameter names and values
  809. // Set the content type
  810. if(!hasContentTypeHeader) {
  811. post.setHeader(HEADER_CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED);
  812. }
  813. // Add the parameters
  814. PropertyIterator args = getArguments().iterator();
  815. List <NameValuePair> nvps = new ArrayList <NameValuePair>();
  816. String urlContentEncoding = contentEncoding;
  817. if(urlContentEncoding == null || urlContentEncoding.length() == 0) {
  818. // Use the default encoding for urls
  819. urlContentEncoding = EncoderCache.URL_ARGUMENT_ENCODING;
  820. }
  821. while (args.hasNext()) {
  822. HTTPArgument arg = (HTTPArgument) args.next().getObjectValue();
  823. // The HTTPClient always urlencodes both name and value,
  824. // so if the argument is already encoded, we have to decode
  825. // it before adding it to the post request
  826. String parameterName = arg.getName();
  827. if (arg.isSkippable(parameterName)){
  828. continue;
  829. }
  830. String parameterValue = arg.getValue();
  831. if(!arg.isAlwaysEncoded()) {
  832. // The value is already encoded by the user
  833. // Must decode the value now, so that when the
  834. // httpclient encodes it, we end up with the same value
  835. // as the user had entered.
  836. parameterName = URLDecoder.decode(parameterName, urlContentEncoding);
  837. parameterValue = URLDecoder.decode(parameterValue, urlContentEncoding);
  838. }
  839. // Add the parameter, httpclient will urlencode it
  840. nvps.add(new BasicNameValuePair(parameterName, parameterValue));
  841. }
  842. UrlEncodedFormEntity entity = new UrlEncodedFormEntity(nvps, urlContentEncoding);
  843. post.setEntity(entity);
  844. if (entity.isRepeatable()){
  845. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  846. post.getEntity().writeTo(bos);
  847. bos.flush();
  848. // We get the posted bytes using the encoding used to create it
  849. if (contentEncoding != null) {
  850. postedBody.append(new String(bos.toByteArray(), contentEncoding));
  851. } else {
  852. postedBody.append(new String(bos.toByteArray()));
  853. }
  854. bos.close();
  855. } else {
  856. postedBody.append("<RequestEntity was not repeatable, cannot view what was sent>");
  857. }
  858. }
  859. // // If the request entity is repeatable, we can send it first to
  860. // // our own stream, so we can return it
  861. // if(post.getEntity().isRepeatable()) {
  862. // ByteArrayOutputStream bos = new ByteArrayOutputStream();
  863. // post.getEntity().writeTo(bos);
  864. // bos.flush();
  865. // // We get the posted bytes using the encoding used to create it
  866. // if (contentEncoding != null) {
  867. // postedBody.append(new String(bos.toByteArray(), contentEncoding));
  868. // } else {
  869. // postedBody.append(new String(bos.toByteArray()));
  870. // }
  871. // bos.close();
  872. // }
  873. // else {
  874. // postedBody.append("<RequestEntity was not repeatable, cannot view what was sent>");
  875. // }
  876. }
  877. }
  878. return postedBody.toString();
  879. }
  880. // TODO - implementation not fully tested
  881. private String sendPutData(HttpPut put) throws IOException {
  882. // Buffer to hold the put body, except file content
  883. StringBuilder putBody = new StringBuilder(1000);
  884. boolean hasPutBody = false;
  885. // Check if the header manager had a content type header
  886. // This allows the user to specify his own content-type
  887. Header contentTypeHeader = put.getFirstHeader(HEADER_CONTENT_TYPE);
  888. boolean hasContentTypeHeader = contentTypeHeader != null && contentTypeHeader.getValue() != null && contentTypeHeader.getValue().length() > 0;
  889. // Check for local contentEncoding override
  890. final String contentEncoding = getContentEncoding();
  891. boolean haveContentEncoding = (contentEncoding != null && contentEncoding.trim().length() > 0);
  892. HttpParams putParams = put.getParams();
  893. HTTPFileArg files[] = getHTTPFiles();
  894. // If there are no arguments, we can send a file as the body of the request
  895. if(!hasArguments() && getSendFileAsPostBody()) {
  896. hasPutBody = true;
  897. // If getSendFileAsPostBody returned true, it's sure that file is not null
  898. FileEntity fileRequestEntity = new FileEntity(new File(files[0].getPath()), (String) null); // TODO is null correct?
  899. put.setEntity(fileRequestEntity);
  900. // We just add placeholder text for file content
  901. putBody.append("<actual file content, not shown here>");
  902. }
  903. // If none of the arguments have a name specified, we
  904. // just send all the values as the put body
  905. else if(getSendParameterValuesAsPostBody()) {
  906. hasPutBody = true;
  907. // If a content encoding is specified, we set it as http parameter, so that
  908. // the post body will be encoded in the specified content encoding
  909. if(haveContentEncoding) {
  910. putParams.setParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET,contentEncoding);
  911. }
  912. // Just append all the parameter values, and use that as the post body
  913. StringBuilder putBodyContent = new StringBuilder();
  914. PropertyIterator args = getArguments().iterator();
  915. while (args.hasNext()) {
  916. HTTPArgument arg = (HTTPArgument) args.next().getObjectValue();
  917. String value = null;
  918. if (haveContentEncoding){
  919. value = arg.getEncodedValue(contentEncoding);
  920. } else {
  921. value = arg.getEncodedValue();
  922. }
  923. putBodyContent.append(value);
  924. }
  925. String contentTypeValue = null;
  926. if(hasContentTypeHeader) {
  927. contentTypeValue = put.getFirstHeader(HEADER_CONTENT_TYPE).getValue();
  928. }
  929. StringEntity requestEntity = new StringEntity(putBodyContent.toString(), contentTypeValue,
  930. (String) putParams.getParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET));
  931. put.setEntity(requestEntity);
  932. }
  933. // Check if we have any content to send for body
  934. if(hasPutBody) {
  935. // If the request entity is repeatable, we can send it first to
  936. // our own stream, so we can return it
  937. if(put.getEntity().isRepeatable()) {
  938. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  939. put.getEntity().writeTo(bos);
  940. bos.flush();
  941. // We get the posted bytes using the charset that was used to create them
  942. putBody.append(new String(bos.toByteArray(),
  943. (String) putParams.getParameter(CoreProtocolPNames.HTTP_CONTENT_CHARSET)));
  944. bos.close();
  945. }
  946. else {
  947. putBody.append("<RequestEntity was not repeatable, cannot view what was sent>");
  948. }
  949. if(!hasContentTypeHeader) {
  950. // Allow the mimetype of the file to control the content type
  951. // This is not obvious in GUI if you are not uploading any files,
  952. // but just sending the content of nameless parameters
  953. // TODO: needs a multiple file upload scenerio
  954. HTTPFileArg file = files.length > 0? files[0] : null;
  955. if(file != null && file.getMimeType() != null && file.getMimeType().length() > 0) {
  956. put.setHeader(HEADER_CONTENT_TYPE, file.getMimeType());
  957. }
  958. }
  959. return putBody.toString();
  960. }
  961. return null;
  962. }
  963. private void saveConnectionCookies(HttpResponse method, URL u, CookieManager cookieManager) {
  964. if (cookieManager != null) {
  965. Header[] hdrs = method.getHeaders(HEADER_SET_COOKIE);
  966. for (Header hdr : hdrs) {