PageRenderTime 60ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

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

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 1127 lines | 707 code | 120 blank | 300 comment | 197 complexity | fc075dfbcd14da33ac59669b386bb0ec 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. package org.apache.jmeter.protocol.http.sampler;
  18. import java.io.ByteArrayOutputStream;
  19. import java.io.File;
  20. import java.io.FileNotFoundException;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.io.OutputStream;
  24. import java.net.InetAddress;
  25. import java.net.URL;
  26. import java.net.URLDecoder;
  27. import java.util.ArrayList;
  28. import java.util.HashMap;
  29. import java.util.Map;
  30. import java.util.zip.GZIPInputStream;
  31. import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
  32. import org.apache.commons.httpclient.Header;
  33. import org.apache.commons.httpclient.HostConfiguration;
  34. import org.apache.commons.httpclient.HttpClient;
  35. import org.apache.commons.httpclient.HttpConnectionManager;
  36. import org.apache.commons.httpclient.HttpMethod;
  37. import org.apache.commons.httpclient.HttpMethodBase;
  38. import org.apache.commons.httpclient.HttpState;
  39. import org.apache.commons.httpclient.HttpVersion;
  40. import org.apache.commons.httpclient.NTCredentials;
  41. import org.apache.commons.httpclient.ProtocolException;
  42. import org.apache.commons.httpclient.SimpleHttpConnectionManager;
  43. import org.apache.commons.httpclient.auth.AuthScope;
  44. import org.apache.commons.httpclient.cookie.CookiePolicy;
  45. import org.apache.commons.httpclient.methods.DeleteMethod;
  46. import org.apache.commons.httpclient.methods.FileRequestEntity;
  47. import org.apache.commons.httpclient.methods.GetMethod;
  48. import org.apache.commons.httpclient.methods.HeadMethod;
  49. import org.apache.commons.httpclient.methods.OptionsMethod;
  50. import org.apache.commons.httpclient.methods.PostMethod;
  51. import org.apache.commons.httpclient.methods.PutMethod;
  52. import org.apache.commons.httpclient.methods.StringRequestEntity;
  53. import org.apache.commons.httpclient.methods.TraceMethod;
  54. import org.apache.commons.httpclient.methods.multipart.FilePart;
  55. import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
  56. import org.apache.commons.httpclient.methods.multipart.Part;
  57. import org.apache.commons.httpclient.methods.multipart.PartBase;
  58. import org.apache.commons.httpclient.methods.multipart.StringPart;
  59. import org.apache.commons.httpclient.params.DefaultHttpParams;
  60. import org.apache.commons.httpclient.params.HttpClientParams;
  61. import org.apache.commons.httpclient.params.HttpMethodParams;
  62. import org.apache.commons.httpclient.params.HttpParams;
  63. import org.apache.commons.httpclient.protocol.Protocol;
  64. import org.apache.commons.io.input.CountingInputStream;
  65. import org.apache.jmeter.protocol.http.control.AuthManager;
  66. import org.apache.jmeter.protocol.http.control.Authorization;
  67. import org.apache.jmeter.protocol.http.control.CacheManager;
  68. import org.apache.jmeter.protocol.http.control.CookieManager;
  69. import org.apache.jmeter.protocol.http.control.HeaderManager;
  70. import org.apache.jmeter.protocol.http.util.EncoderCache;
  71. import org.apache.jmeter.protocol.http.util.HTTPArgument;
  72. import org.apache.jmeter.protocol.http.util.HTTPFileArg;
  73. import org.apache.jmeter.protocol.http.util.LoopbackHttpClientSocketFactory;
  74. import org.apache.jmeter.protocol.http.util.SlowHttpClientSocketFactory;
  75. import org.apache.jmeter.testelement.property.CollectionProperty;
  76. import org.apache.jmeter.testelement.property.PropertyIterator;
  77. import org.apache.jmeter.util.JMeterUtils;
  78. import org.apache.jmeter.util.SSLManager;
  79. import org.apache.jorphan.logging.LoggingManager;
  80. import org.apache.jorphan.util.JOrphanUtils;
  81. import org.apache.log.Logger;
  82. /**
  83. * HTTP sampler using Apache (Jakarta) Commons HttpClient 3.1.
  84. */
  85. public class HTTPHC3Impl extends HTTPHCAbstractImpl {
  86. private static final Logger log = LoggingManager.getLoggerForClass();
  87. /** retry count to be used (default 1); 0 = disable retries */
  88. private static final int RETRY_COUNT = JMeterUtils.getPropDefault("httpclient3.retrycount", 1);
  89. private static final String HTTP_AUTHENTICATION_PREEMPTIVE = "http.authentication.preemptive"; // $NON-NLS-1$
  90. private static final boolean canSetPreEmptive; // OK to set pre-emptive auth?
  91. private static final ThreadLocal<Map<HostConfiguration, HttpClient>> httpClients =
  92. new ThreadLocal<Map<HostConfiguration, HttpClient>>(){
  93. @Override
  94. protected Map<HostConfiguration, HttpClient> initialValue() {
  95. return new HashMap<HostConfiguration, HttpClient>();
  96. }
  97. };
  98. // Needs to be accessible by HTTPSampler2
  99. volatile HttpClient savedClient;
  100. static {
  101. log.info("HTTP request retry count = "+RETRY_COUNT);
  102. if (CPS_HTTP > 0) {
  103. log.info("Setting up HTTP SlowProtocol, cps="+CPS_HTTP);
  104. Protocol.registerProtocol(PROTOCOL_HTTP,
  105. new Protocol(PROTOCOL_HTTP,new SlowHttpClientSocketFactory(CPS_HTTP),DEFAULT_HTTP_PORT));
  106. }
  107. // Now done in JsseSSLManager (which needs to register the protocol)
  108. // cps =
  109. // JMeterUtils.getPropDefault("httpclient.socket.https.cps", 0); // $NON-NLS-1$
  110. //
  111. // if (cps > 0) {
  112. // log.info("Setting up HTTPS SlowProtocol, cps="+cps);
  113. // Protocol.registerProtocol(PROTOCOL_HTTPS,
  114. // new Protocol(PROTOCOL_HTTPS,new SlowHttpClientSocketFactory(cps),DEFAULT_HTTPS_PORT));
  115. // }
  116. // Set default parameters as needed
  117. HttpParams params = DefaultHttpParams.getDefaultParams();
  118. // Process Commons HttpClient parameters file
  119. String file=JMeterUtils.getProperty("httpclient.parameters.file"); // $NON-NLS-1$
  120. if (file != null) {
  121. HttpClientDefaultParameters.load(file, params);
  122. }
  123. // If the pre-emptive parameter is undefined, then we can set it as needed
  124. // otherwise we should do what the user requested.
  125. canSetPreEmptive = params.getParameter(HTTP_AUTHENTICATION_PREEMPTIVE) == null;
  126. // Handle old-style JMeter properties
  127. try {
  128. params.setParameter(HttpMethodParams.PROTOCOL_VERSION, HttpVersion.parse("HTTP/"+HTTP_VERSION));
  129. } catch (ProtocolException e) {
  130. log.warn("Problem setting protocol version "+e.getLocalizedMessage());
  131. }
  132. if (SO_TIMEOUT >= 0){
  133. params.setIntParameter(HttpMethodParams.SO_TIMEOUT, SO_TIMEOUT);
  134. }
  135. // This must be done last, as must not be overridden
  136. params.setParameter(HttpMethodParams.COOKIE_POLICY,CookiePolicy.IGNORE_COOKIES);
  137. // We do our own cookie handling
  138. if (USE_LOOPBACK){
  139. LoopbackHttpClientSocketFactory.setup();
  140. }
  141. }
  142. protected HTTPHC3Impl(HTTPSamplerBase base) {
  143. super(base);
  144. }
  145. /**
  146. * Samples the URL passed in and stores the result in
  147. * <code>HTTPSampleResult</code>, following redirects and downloading
  148. * page resources as appropriate.
  149. * <p>
  150. * When getting a redirect target, redirects are not followed and resources
  151. * are not downloaded. The caller will take care of this.
  152. *
  153. * @param url
  154. * URL to sample
  155. * @param method
  156. * HTTP method: GET, POST,...
  157. * @param areFollowingRedirect
  158. * whether we're getting a redirect target
  159. * @param frameDepth
  160. * Depth of this target in the frame structure. Used only to
  161. * prevent infinite recursion.
  162. * @return results of the sampling
  163. */
  164. @Override
  165. protected HTTPSampleResult sample(URL url, String method, boolean areFollowingRedirect, int frameDepth) {
  166. String urlStr = url.toString();
  167. log.debug("Start : sample " + urlStr);
  168. log.debug("method " + method);
  169. HttpMethodBase httpMethod = null;
  170. HTTPSampleResult res = new HTTPSampleResult();
  171. res.setMonitor(isMonitor());
  172. res.setSampleLabel(urlStr); // May be replaced later
  173. res.setHTTPMethod(method);
  174. res.setURL(url);
  175. res.sampleStart(); // Count the retries as well in the time
  176. try {
  177. // May generate IllegalArgumentException
  178. if (method.equals(POST)) {
  179. httpMethod = new PostMethod(urlStr);
  180. } else if (method.equals(PUT)){
  181. httpMethod = new PutMethod(urlStr);
  182. } else if (method.equals(HEAD)){
  183. httpMethod = new HeadMethod(urlStr);
  184. } else if (method.equals(TRACE)){
  185. httpMethod = new TraceMethod(urlStr);
  186. } else if (method.equals(OPTIONS)){
  187. httpMethod = new OptionsMethod(urlStr);
  188. } else if (method.equals(DELETE)){
  189. httpMethod = new DeleteMethod(urlStr);
  190. } else if (method.equals(GET)){
  191. httpMethod = new GetMethod(urlStr);
  192. } else {
  193. throw new IllegalArgumentException("Unexpected method: "+method);
  194. }
  195. final CacheManager cacheManager = getCacheManager();
  196. if (cacheManager != null && GET.equalsIgnoreCase(method)) {
  197. if (cacheManager.inCache(url)) {
  198. res.sampleEnd();
  199. res.setResponseNoContent();
  200. res.setSuccessful(true);
  201. return res;
  202. }
  203. }
  204. // Set any default request headers
  205. setDefaultRequestHeaders(httpMethod);
  206. // Setup connection
  207. HttpClient client = setupConnection(url, httpMethod, res);
  208. savedClient = client;
  209. // Handle the various methods
  210. if (method.equals(POST)) {
  211. String postBody = sendPostData((PostMethod)httpMethod);
  212. res.setQueryString(postBody);
  213. } else if (method.equals(PUT)) {
  214. String putBody = sendPutData((PutMethod)httpMethod);
  215. res.setQueryString(putBody);
  216. }
  217. int statusCode = client.executeMethod(httpMethod);
  218. // Needs to be done after execute to pick up all the headers
  219. res.setRequestHeaders(getConnectionHeaders(httpMethod));
  220. // Request sent. Now get the response:
  221. InputStream instream = httpMethod.getResponseBodyAsStream();
  222. if (instream != null) {// will be null for HEAD
  223. instream = new CountingInputStream(instream);
  224. try {
  225. Header responseHeader = httpMethod.getResponseHeader(HEADER_CONTENT_ENCODING);
  226. if (responseHeader!= null && ENCODING_GZIP.equals(responseHeader.getValue())) {
  227. InputStream tmpInput = new GZIPInputStream(instream); // tmp inputstream needs to have a good counting
  228. res.setResponseData(readResponse(res, tmpInput, (int) httpMethod.getResponseContentLength()));
  229. } else {
  230. res.setResponseData(readResponse(res, instream, (int) httpMethod.getResponseContentLength()));
  231. }
  232. } finally {
  233. JOrphanUtils.closeQuietly(instream);
  234. }
  235. }
  236. res.sampleEnd();
  237. // Done with the sampling proper.
  238. // Now collect the results into the HTTPSampleResult:
  239. res.setSampleLabel(httpMethod.getURI().toString());
  240. // Pick up Actual path (after redirects)
  241. res.setResponseCode(Integer.toString(statusCode));
  242. res.setSuccessful(isSuccessCode(statusCode));
  243. res.setResponseMessage(httpMethod.getStatusText());
  244. String ct = null;
  245. Header h = httpMethod.getResponseHeader(HEADER_CONTENT_TYPE);
  246. if (h != null)// Can be missing, e.g. on redirect
  247. {
  248. ct = h.getValue();
  249. res.setContentType(ct);// e.g. text/html; charset=ISO-8859-1
  250. res.setEncodingAndType(ct);
  251. }
  252. res.setResponseHeaders(getResponseHeaders(httpMethod));
  253. if (res.isRedirect()) {
  254. final Header headerLocation = httpMethod.getResponseHeader(HEADER_LOCATION);
  255. if (headerLocation == null) { // HTTP protocol violation, but avoids NPE
  256. throw new IllegalArgumentException("Missing location header");
  257. }
  258. res.setRedirectLocation(headerLocation.getValue());
  259. }
  260. // record some sizes to allow HTTPSampleResult.getBytes() with different options
  261. if (instream != null) {
  262. res.setBodySize(((CountingInputStream) instream).getCount());
  263. }
  264. res.setHeadersSize(calculateHeadersSize(httpMethod));
  265. if (log.isDebugEnabled()) {
  266. log.debug("Response headersSize=" + res.getHeadersSize() + " bodySize=" + res.getBodySize()
  267. + " Total=" + (res.getHeadersSize() + res.getBodySize()));
  268. }
  269. // If we redirected automatically, the URL may have changed
  270. if (getAutoRedirects()){
  271. res.setURL(new URL(httpMethod.getURI().toString()));
  272. }
  273. // Store any cookies received in the cookie manager:
  274. saveConnectionCookies(httpMethod, res.getURL(), getCookieManager());
  275. // Save cache information
  276. if (cacheManager != null){
  277. cacheManager.saveDetails(httpMethod, res);
  278. }
  279. // Follow redirects and download page resources if appropriate:
  280. res = resultProcessing(areFollowingRedirect, frameDepth, res);
  281. log.debug("End : sample");
  282. return res;
  283. } catch (IllegalArgumentException e)// e.g. some kinds of invalid URL
  284. {
  285. res.sampleEnd();
  286. HTTPSampleResult err = errorResult(e, res);
  287. err.setSampleLabel("Error: " + url.toString());
  288. return err;
  289. } catch (IOException e) {
  290. res.sampleEnd();
  291. HTTPSampleResult err = errorResult(e, res);
  292. err.setSampleLabel("Error: " + url.toString());
  293. return err;
  294. } finally {
  295. savedClient = null;
  296. if (httpMethod != null) {
  297. httpMethod.releaseConnection();
  298. }
  299. }
  300. }
  301. /**
  302. * Calculate response headers size
  303. *
  304. * @return the size response headers (in bytes)
  305. */
  306. private static int calculateHeadersSize(HttpMethodBase httpMethod) {
  307. int headerSize = httpMethod.getStatusLine().toString().length()+2; // add a \r\n
  308. Header[] rh = httpMethod.getResponseHeaders();
  309. for (int i = 0; i < rh.length; i++) {
  310. headerSize += (rh[i]).toString().length(); // already include the \r\n
  311. }
  312. headerSize += 2; // last \r\n before response data
  313. return headerSize;
  314. }
  315. /**
  316. * Returns an <code>HttpConnection</code> fully ready to attempt
  317. * connection. This means it sets the request method (GET or POST), headers,
  318. * cookies, and authorization for the URL request.
  319. * <p>
  320. * The request infos are saved into the sample result if one is provided.
  321. *
  322. * @param u
  323. * <code>URL</code> of the URL request
  324. * @param httpMethod
  325. * GET/PUT/HEAD etc
  326. * @param res
  327. * sample result to save request infos to
  328. * @return <code>HttpConnection</code> ready for .connect
  329. * @exception IOException
  330. * if an I/O Exception occurs
  331. */
  332. protected HttpClient setupConnection(URL u, HttpMethodBase httpMethod, HTTPSampleResult res) throws IOException {
  333. String urlStr = u.toString();
  334. org.apache.commons.httpclient.URI uri = new org.apache.commons.httpclient.URI(urlStr,false);
  335. String schema = uri.getScheme();
  336. if ((schema == null) || (schema.length()==0)) {
  337. schema = PROTOCOL_HTTP;
  338. }
  339. if (PROTOCOL_HTTPS.equalsIgnoreCase(schema)){
  340. SSLManager.getInstance(); // ensure the manager is initialised
  341. // we don't currently need to do anything further, as this sets the default https protocol
  342. }
  343. Protocol protocol = Protocol.getProtocol(schema);
  344. String host = uri.getHost();
  345. int port = uri.getPort();
  346. /*
  347. * We use the HostConfiguration as the key to retrieve the HttpClient,
  348. * so need to ensure that any items used in its equals/hashcode methods are
  349. * not changed after use, i.e.:
  350. * host, port, protocol, localAddress, proxy
  351. *
  352. */
  353. HostConfiguration hc = new HostConfiguration();
  354. hc.setHost(host, port, protocol); // All needed to ensure re-usablility
  355. // Set up the local address if one exists
  356. if (localAddress != null){
  357. hc.setLocalAddress(localAddress);
  358. } else {
  359. final String ipSource = getIpSource();
  360. if (ipSource.length() > 0) {// Use special field ip source address (for pseudo 'ip spoofing')
  361. InetAddress inetAddr = InetAddress.getByName(ipSource);
  362. hc.setLocalAddress(inetAddr);
  363. }
  364. }
  365. final String proxyHost = getProxyHost();
  366. final int proxyPort = getProxyPortInt();
  367. boolean useStaticProxy = isStaticProxy(host);
  368. boolean useDynamicProxy = isDynamicProxy(proxyHost, proxyPort);
  369. if (useDynamicProxy){
  370. hc.setProxy(proxyHost, proxyPort);
  371. useStaticProxy = false; // Dynamic proxy overrules static proxy
  372. } else if (useStaticProxy) {
  373. if (log.isDebugEnabled()){
  374. log.debug("Setting proxy: "+PROXY_HOST+":"+PROXY_PORT);
  375. }
  376. hc.setProxy(PROXY_HOST, PROXY_PORT);
  377. }
  378. Map<HostConfiguration, HttpClient> map = httpClients.get();
  379. // N.B. HostConfiguration.equals() includes proxy settings in the compare.
  380. HttpClient httpClient = map.get(hc);
  381. if ( httpClient == null )
  382. {
  383. httpClient = new HttpClient(new SimpleHttpConnectionManager());
  384. httpClient.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
  385. new DefaultHttpMethodRetryHandler(RETRY_COUNT, false));
  386. if (log.isDebugEnabled()) {
  387. log.debug("Created new HttpClient: @"+System.identityHashCode(httpClient));
  388. }
  389. httpClient.setHostConfiguration(hc);
  390. map.put(hc, httpClient);
  391. } else {
  392. if (log.isDebugEnabled()) {
  393. log.debug("Reusing the HttpClient: @"+System.identityHashCode(httpClient));
  394. }
  395. }
  396. // Set up any required Proxy credentials
  397. if (useDynamicProxy){
  398. String user = getProxyUser();
  399. if (user.length() > 0){
  400. httpClient.getState().setProxyCredentials(
  401. new AuthScope(proxyHost,proxyPort,null,AuthScope.ANY_SCHEME),
  402. new NTCredentials(user,getProxyPass(),localHost,PROXY_DOMAIN)
  403. );
  404. } else {
  405. httpClient.getState().clearProxyCredentials();
  406. }
  407. } else {
  408. if (useStaticProxy) {
  409. if (PROXY_USER.length() > 0){
  410. httpClient.getState().setProxyCredentials(
  411. new AuthScope(PROXY_HOST,PROXY_PORT,null,AuthScope.ANY_SCHEME),
  412. new NTCredentials(PROXY_USER,PROXY_PASS,localHost,PROXY_DOMAIN)
  413. );
  414. }
  415. } else {
  416. httpClient.getState().clearProxyCredentials();
  417. }
  418. }
  419. int rto = getResponseTimeout();
  420. if (rto > 0){
  421. httpMethod.getParams().setSoTimeout(rto);
  422. }
  423. int cto = getConnectTimeout();
  424. if (cto > 0){
  425. httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(cto);
  426. }
  427. // Allow HttpClient to handle the redirects:
  428. httpMethod.setFollowRedirects(getAutoRedirects());
  429. // a well-behaved browser is supposed to send 'Connection: close'
  430. // with the last request to an HTTP server. Instead, most browsers
  431. // leave it to the server to close the connection after their
  432. // timeout period. Leave it to the JMeter user to decide.
  433. if (getUseKeepAlive()) {
  434. httpMethod.setRequestHeader(HEADER_CONNECTION, KEEP_ALIVE);
  435. } else {
  436. httpMethod.setRequestHeader(HEADER_CONNECTION, CONNECTION_CLOSE);
  437. }
  438. setConnectionHeaders(httpMethod, u, getHeaderManager(), getCacheManager());
  439. String cookies = setConnectionCookie(httpMethod, u, getCookieManager());
  440. setConnectionAuthorization(httpClient, u, getAuthManager());
  441. if (res != null) {
  442. res.setCookies(cookies);
  443. }
  444. return httpClient;
  445. }
  446. /**
  447. * Set any default request headers to include
  448. *
  449. * @param httpMethod the HttpMethod used for the request
  450. */
  451. protected void setDefaultRequestHeaders(HttpMethod httpMethod) {
  452. // Method left empty here, but allows subclasses to override
  453. }
  454. /**
  455. * Gets the ResponseHeaders
  456. *
  457. * @param method the method used to perform the request
  458. * @return string containing the headers, one per line
  459. */
  460. protected String getResponseHeaders(HttpMethod method) {
  461. StringBuilder headerBuf = new StringBuilder();
  462. org.apache.commons.httpclient.Header rh[] = method.getResponseHeaders();
  463. headerBuf.append(method.getStatusLine());// header[0] is not the status line...
  464. headerBuf.append("\n"); // $NON-NLS-1$
  465. for (int i = 0; i < rh.length; i++) {
  466. String key = rh[i].getName();
  467. headerBuf.append(key);
  468. headerBuf.append(": "); // $NON-NLS-1$
  469. headerBuf.append(rh[i].getValue());
  470. headerBuf.append("\n"); // $NON-NLS-1$
  471. }
  472. return headerBuf.toString();
  473. }
  474. /**
  475. * Extracts all the required cookies for that particular URL request and
  476. * sets them in the <code>HttpMethod</code> passed in.
  477. *
  478. * @param method <code>HttpMethod</code> for the request
  479. * @param u <code>URL</code> of the request
  480. * @param cookieManager the <code>CookieManager</code> containing all the cookies
  481. * @return a String containing the cookie details (for the response)
  482. * May be null
  483. */
  484. private String setConnectionCookie(HttpMethod method, URL u, CookieManager cookieManager) {
  485. String cookieHeader = null;
  486. if (cookieManager != null) {
  487. cookieHeader = cookieManager.getCookieHeaderForURL(u);
  488. if (cookieHeader != null) {
  489. method.setRequestHeader(HEADER_COOKIE, cookieHeader);
  490. }
  491. }
  492. return cookieHeader;
  493. }
  494. /**
  495. * Extracts all the required non-cookie headers for that particular URL request and
  496. * sets them in the <code>HttpMethod</code> passed in
  497. *
  498. * @param method
  499. * <code>HttpMethod</code> which represents the request
  500. * @param u
  501. * <code>URL</code> of the URL request
  502. * @param headerManager
  503. * the <code>HeaderManager</code> containing all the cookies
  504. * for this <code>UrlConfig</code>
  505. * @param cacheManager the CacheManager (may be null)
  506. */
  507. private void setConnectionHeaders(HttpMethod method, URL u, HeaderManager headerManager, CacheManager cacheManager) {
  508. // Set all the headers from the HeaderManager
  509. if (headerManager != null) {
  510. CollectionProperty headers = headerManager.getHeaders();
  511. if (headers != null) {
  512. PropertyIterator i = headers.iterator();
  513. while (i.hasNext()) {
  514. org.apache.jmeter.protocol.http.control.Header header
  515. = (org.apache.jmeter.protocol.http.control.Header)
  516. i.next().getObjectValue();
  517. String n = header.getName();
  518. // Don't allow override of Content-Length
  519. // This helps with SoapSampler hack too
  520. // TODO - what other headers are not allowed?
  521. if (! HEADER_CONTENT_LENGTH.equalsIgnoreCase(n)){
  522. String v = header.getValue();
  523. if (HEADER_HOST.equalsIgnoreCase(n)) {
  524. v = v.replaceFirst(":\\d+$",""); // remove any port specification // $NON-NLS-1$ $NON-NLS-2$
  525. method.getParams().setVirtualHost(v);
  526. } else {
  527. method.addRequestHeader(n, v);
  528. }
  529. }
  530. }
  531. }
  532. }
  533. if (cacheManager != null){
  534. cacheManager.setHeaders(u, method);
  535. }
  536. }
  537. /**
  538. * Get all the request headers for the <code>HttpMethod</code>
  539. *
  540. * @param method
  541. * <code>HttpMethod</code> which represents the request
  542. * @return the headers as a string
  543. */
  544. protected String getConnectionHeaders(HttpMethod method) {
  545. // Get all the request headers
  546. StringBuilder hdrs = new StringBuilder(100);
  547. Header[] requestHeaders = method.getRequestHeaders();
  548. for(int i = 0; i < requestHeaders.length; i++) {
  549. // Exclude the COOKIE header, since cookie is reported separately in the sample
  550. if(!HEADER_COOKIE.equalsIgnoreCase(requestHeaders[i].getName())) {
  551. hdrs.append(requestHeaders[i].getName());
  552. hdrs.append(": "); // $NON-NLS-1$
  553. hdrs.append(requestHeaders[i].getValue());
  554. hdrs.append("\n"); // $NON-NLS-1$
  555. }
  556. }
  557. return hdrs.toString();
  558. }
  559. /**
  560. * Extracts all the required authorization for that particular URL request
  561. * and sets it in the <code>HttpMethod</code> passed in.
  562. *
  563. * @param client the HttpClient object
  564. *
  565. * @param u
  566. * <code>URL</code> of the URL request
  567. * @param authManager
  568. * the <code>AuthManager</code> containing all the authorisations for
  569. * this <code>UrlConfig</code>
  570. */
  571. private void setConnectionAuthorization(HttpClient client, URL u, AuthManager authManager) {
  572. HttpState state = client.getState();
  573. if (authManager != null) {
  574. HttpClientParams params = client.getParams();
  575. Authorization auth = authManager.getAuthForURL(u);
  576. if (auth != null) {
  577. String username = auth.getUser();
  578. String realm = auth.getRealm();
  579. String domain = auth.getDomain();
  580. if (log.isDebugEnabled()){
  581. log.debug(username + " > D="+ username + " D="+domain+" R="+realm);
  582. }
  583. state.setCredentials(
  584. new AuthScope(u.getHost(),u.getPort(),
  585. realm.length()==0 ? null : realm //"" is not the same as no realm
  586. ,AuthScope.ANY_SCHEME),
  587. // NT Includes other types of Credentials
  588. new NTCredentials(
  589. username,
  590. auth.getPass(),
  591. localHost,
  592. domain
  593. ));
  594. // We have credentials - should we set pre-emptive authentication?
  595. if (canSetPreEmptive){
  596. log.debug("Setting Pre-emptive authentication");
  597. params.setAuthenticationPreemptive(true);
  598. }
  599. } else {
  600. state.clearCredentials();
  601. if (canSetPreEmptive){
  602. params.setAuthenticationPreemptive(false);
  603. }
  604. }
  605. } else {
  606. state.clearCredentials();
  607. }
  608. }
  609. /*
  610. * Send POST data from <code>Entry</code> to the open connection.
  611. *
  612. * @param connection
  613. * <code>URLConnection</code> where POST data should be sent
  614. * @return a String show what was posted. Will not contain actual file upload content
  615. * @exception IOException
  616. * if an I/O exception occurs
  617. */
  618. private String sendPostData(PostMethod post) throws IOException {
  619. // Buffer to hold the post body, except file content
  620. StringBuilder postedBody = new StringBuilder(1000);
  621. HTTPFileArg files[] = getHTTPFiles();
  622. // Check if we should do a multipart/form-data or an
  623. // application/x-www-form-urlencoded post request
  624. if(getUseMultipartForPost()) {
  625. // If a content encoding is specified, we use that as the
  626. // encoding of any parameter values
  627. String contentEncoding = getContentEncoding();
  628. if(contentEncoding != null && contentEncoding.length() == 0) {
  629. contentEncoding = null;
  630. }
  631. final boolean browserCompatible = getDoBrowserCompatibleMultipart();
  632. // We don't know how many entries will be skipped
  633. ArrayList<PartBase> partlist = new ArrayList<PartBase>();
  634. // Create the parts
  635. // Add any parameters
  636. PropertyIterator args = getArguments().iterator();
  637. while (args.hasNext()) {
  638. HTTPArgument arg = (HTTPArgument) args.next().getObjectValue();
  639. String parameterName = arg.getName();
  640. if (arg.isSkippable(parameterName)){
  641. continue;
  642. }
  643. StringPart part = new StringPart(arg.getName(), arg.getValue(), contentEncoding);
  644. if (browserCompatible) {
  645. part.setTransferEncoding(null);
  646. part.setContentType(null);
  647. }
  648. partlist.add(part);
  649. }
  650. // Add any files
  651. for (int i=0; i < files.length; i++) {
  652. HTTPFileArg file = files[i];
  653. File inputFile = new File(file.getPath());
  654. // We do not know the char set of the file to be uploaded, so we set it to null
  655. ViewableFilePart filePart = new ViewableFilePart(file.getParamName(), inputFile, file.getMimeType(), null);
  656. filePart.setCharSet(null); // We do not know what the char set of the file is
  657. partlist.add(filePart);
  658. }
  659. // Set the multipart for the post
  660. int partNo = partlist.size();
  661. Part[] parts = partlist.toArray(new Part[partNo]);
  662. MultipartRequestEntity multiPart = new MultipartRequestEntity(parts, post.getParams());
  663. post.setRequestEntity(multiPart);
  664. // Set the content type
  665. String multiPartContentType = multiPart.getContentType();
  666. post.setRequestHeader(HEADER_CONTENT_TYPE, multiPartContentType);
  667. // If the Multipart is repeatable, we can send it first to
  668. // our own stream, without the actual file content, so we can return it
  669. if(multiPart.isRepeatable()) {
  670. // For all the file multiparts, we must tell it to not include
  671. // the actual file content
  672. for(int i = 0; i < partNo; i++) {
  673. if(parts[i] instanceof ViewableFilePart) {
  674. ((ViewableFilePart) parts[i]).setHideFileData(true); // .sendMultipartWithoutFileContent(bos);
  675. }
  676. }
  677. // Write the request to our own stream
  678. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  679. multiPart.writeRequest(bos);
  680. bos.flush();
  681. // We get the posted bytes using the encoding used to create it
  682. postedBody.append(new String(bos.toByteArray(),
  683. contentEncoding == null ? "US-ASCII" // $NON-NLS-1$ this is the default used by HttpClient
  684. : contentEncoding));
  685. bos.close();
  686. // For all the file multiparts, we must revert the hiding of
  687. // the actual file content
  688. for(int i = 0; i < partNo; i++) {
  689. if(parts[i] instanceof ViewableFilePart) {
  690. ((ViewableFilePart) parts[i]).setHideFileData(false);
  691. }
  692. }
  693. }
  694. else {
  695. postedBody.append("<Multipart was not repeatable, cannot view what was sent>"); // $NON-NLS-1$
  696. }
  697. }
  698. else {
  699. // Check if the header manager had a content type header
  700. // This allows the user to specify his own content-type for a POST request
  701. Header contentTypeHeader = post.getRequestHeader(HEADER_CONTENT_TYPE);
  702. boolean hasContentTypeHeader = contentTypeHeader != null && contentTypeHeader.getValue() != null && contentTypeHeader.getValue().length() > 0;
  703. // If there are no arguments, we can send a file as the body of the request
  704. // TODO: needs a multiple file upload scenerio
  705. if(!hasArguments() && getSendFileAsPostBody()) {
  706. // If getSendFileAsPostBody returned true, it's sure that file is not null
  707. HTTPFileArg file = files[0];
  708. if(!hasContentTypeHeader) {
  709. // Allow the mimetype of the file to control the content type
  710. if(file.getMimeType() != null && file.getMimeType().length() > 0) {
  711. post.setRequestHeader(HEADER_CONTENT_TYPE, file.getMimeType());
  712. }
  713. else {
  714. post.setRequestHeader(HEADER_CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED);
  715. }
  716. }
  717. FileRequestEntity fileRequestEntity = new FileRequestEntity(new File(file.getPath()),null);
  718. post.setRequestEntity(fileRequestEntity);
  719. // We just add placeholder text for file content
  720. postedBody.append("<actual file content, not shown here>");
  721. }
  722. else {
  723. // In a post request which is not multipart, we only support
  724. // parameters, no file upload is allowed
  725. // If a content encoding is specified, we set it as http parameter, so that
  726. // the post body will be encoded in the specified content encoding
  727. String contentEncoding = getContentEncoding();
  728. boolean haveContentEncoding = false;
  729. if(contentEncoding != null && contentEncoding.trim().length() > 0) {
  730. post.getParams().setContentCharset(contentEncoding);
  731. haveContentEncoding = true;
  732. } else if (contentEncoding != null && contentEncoding.trim().length() == 0){
  733. contentEncoding=null;
  734. }
  735. // If none of the arguments have a name specified, we
  736. // just send all the values as the post body
  737. if(getSendParameterValuesAsPostBody()) {
  738. // Allow the mimetype of the file to control the content type
  739. // This is not obvious in GUI if you are not uploading any files,
  740. // but just sending the content of nameless parameters
  741. // TODO: needs a multiple file upload scenerio
  742. if(!hasContentTypeHeader) {
  743. HTTPFileArg file = files.length > 0? files[0] : null;
  744. if(file != null && file.getMimeType() != null && file.getMimeType().length() > 0) {
  745. post.setRequestHeader(HEADER_CONTENT_TYPE, file.getMimeType());
  746. }
  747. else {
  748. // TODO - is this the correct default?
  749. post.setRequestHeader(HEADER_CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED);
  750. }
  751. }
  752. // Just append all the parameter values, and use that as the post body
  753. StringBuilder postBody = new StringBuilder();
  754. PropertyIterator args = getArguments().iterator();
  755. while (args.hasNext()) {
  756. HTTPArgument arg = (HTTPArgument) args.next().getObjectValue();
  757. String value;
  758. if (haveContentEncoding){
  759. value = arg.getEncodedValue(contentEncoding);
  760. } else {
  761. value = arg.getEncodedValue();
  762. }
  763. postBody.append(value);
  764. }
  765. StringRequestEntity requestEntity = new StringRequestEntity(postBody.toString(), post.getRequestHeader(HEADER_CONTENT_TYPE).getValue(), contentEncoding);
  766. post.setRequestEntity(requestEntity);
  767. }
  768. else {
  769. // It is a normal post request, with parameter names and values
  770. // Set the content type
  771. if(!hasContentTypeHeader) {
  772. post.setRequestHeader(HEADER_CONTENT_TYPE, APPLICATION_X_WWW_FORM_URLENCODED);
  773. }
  774. // Add the parameters
  775. PropertyIterator args = getArguments().iterator();
  776. while (args.hasNext()) {
  777. HTTPArgument arg = (HTTPArgument) args.next().getObjectValue();
  778. // The HTTPClient always urlencodes both name and value,
  779. // so if the argument is already encoded, we have to decode
  780. // it before adding it to the post request
  781. String parameterName = arg.getName();
  782. if (arg.isSkippable(parameterName)){
  783. continue;
  784. }
  785. String parameterValue = arg.getValue();
  786. if(!arg.isAlwaysEncoded()) {
  787. // The value is already encoded by the user
  788. // Must decode the value now, so that when the
  789. // httpclient encodes it, we end up with the same value
  790. // as the user had entered.
  791. String urlContentEncoding = contentEncoding;
  792. if(urlContentEncoding == null || urlContentEncoding.length() == 0) {
  793. // Use the default encoding for urls
  794. urlContentEncoding = EncoderCache.URL_ARGUMENT_ENCODING;
  795. }
  796. parameterName = URLDecoder.decode(parameterName, urlContentEncoding);
  797. parameterValue = URLDecoder.decode(parameterValue, urlContentEncoding);
  798. }
  799. // Add the parameter, httpclient will urlencode it
  800. post.addParameter(parameterName, parameterValue);
  801. }
  802. /*
  803. // // Alternative implementation, to make sure that HTTPSampler and HTTPSampler2
  804. // // sends the same post body.
  805. //
  806. // // Only include the content char set in the content-type header if it is not
  807. // // an APPLICATION_X_WWW_FORM_URLENCODED content type
  808. // String contentCharSet = null;
  809. // if(!post.getRequestHeader(HEADER_CONTENT_TYPE).getValue().equals(APPLICATION_X_WWW_FORM_URLENCODED)) {
  810. // contentCharSet = post.getRequestCharSet();
  811. // }
  812. // StringRequestEntity requestEntity = new StringRequestEntity(getQueryString(contentEncoding), post.getRequestHeader(HEADER_CONTENT_TYPE).getValue(), contentCharSet);
  813. // post.setRequestEntity(requestEntity);
  814. */
  815. }
  816. // If the request entity is repeatable, we can send it first to
  817. // our own stream, so we can return it
  818. if(post.getRequestEntity().isRepeatable()) {
  819. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  820. post.getRequestEntity().writeRequest(bos);
  821. bos.flush();
  822. // We get the posted bytes using the encoding used to create it
  823. postedBody.append(new String(bos.toByteArray(),post.getRequestCharSet()));
  824. bos.close();
  825. }
  826. else {
  827. postedBody.append("<RequestEntity was not repeatable, cannot view what was sent>");
  828. }
  829. }
  830. }
  831. // Set the content length
  832. post.setRequestHeader(HEADER_CONTENT_LENGTH, Long.toString(post.getRequestEntity().getContentLength()));
  833. return postedBody.toString();
  834. }
  835. /**
  836. * Set up the PUT data
  837. */
  838. private String sendPutData(PutMethod put) throws IOException {
  839. // Buffer to hold the put body, except file content
  840. StringBuilder putBody = new StringBuilder(1000);
  841. boolean hasPutBody = false;
  842. // Check if the header manager had a content type header
  843. // This allows the user to specify his own content-type for a POST request
  844. Header contentTypeHeader = put.getRequestHeader(HEADER_CONTENT_TYPE);
  845. boolean hasContentTypeHeader = contentTypeHeader != null && contentTypeHeader.getValue() != null && contentTypeHeader.getValue().length() > 0;
  846. HTTPFileArg files[] = getHTTPFiles();
  847. // If there are no arguments, we can send a file as the body of the request
  848. if(!hasArguments() && getSendFileAsPostBody()) {
  849. hasPutBody = true;
  850. // If getSendFileAsPostBody returned true, it's sure that file is not null
  851. FileRequestEntity fileRequestEntity = new FileRequestEntity(new File(files[0].getPath()),null);
  852. put.setRequestEntity(fileRequestEntity);
  853. // We just add placeholder text for file content
  854. putBody.append("<actual file content, not shown here>");
  855. }
  856. // If none of the arguments have a name specified, we
  857. // just send all the values as the put body
  858. else if(getSendParameterValuesAsPostBody()) {
  859. hasPutBody = true;
  860. // If a content encoding is specified, we set it as http parameter, so that
  861. // the post body will be encoded in the specified content encoding
  862. final String contentEncoding = getContentEncoding();
  863. boolean haveContentEncoding = false;
  864. if(contentEncoding != null && contentEncoding.trim().length() > 0) {
  865. put.getParams().setContentCharset(contentEncoding);
  866. haveContentEncoding = true;
  867. }
  868. // Just append all the parameter values, and use that as the post body
  869. StringBuilder putBodyContent = new StringBuilder();
  870. PropertyIterator args = getArguments().iterator();
  871. while (args.hasNext()) {
  872. HTTPArgument arg = (HTTPArgument) args.next().getObjectValue();
  873. String value = null;
  874. if (haveContentEncoding){
  875. value = arg.getEncodedValue(contentEncoding);
  876. } else {
  877. value = arg.getEncodedValue();
  878. }
  879. putBodyContent.append(value);
  880. }
  881. String contentTypeValue = null;
  882. if(hasContentTypeHeader) {
  883. contentTypeValue = put.getRequestHeader(HEADER_CONTENT_TYPE).getValue();
  884. }
  885. StringRequestEntity requestEntity = new StringRequestEntity(putBodyContent.toString(), contentTypeValue, put.getRequestCharSet());
  886. put.setRequestEntity(requestEntity);
  887. }
  888. // Check if we have any content to send for body
  889. if(hasPutBody) {
  890. // If the request entity is repeatable, we can send it first to
  891. // our own stream, so we can return it
  892. if(put.getRequestEntity().isRepeatable()) {
  893. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  894. put.getRequestEntity().writeRequest(bos);
  895. bos.flush();
  896. // We get the posted bytes using the charset that was used to create them
  897. putBody.append(new String(bos.toByteArray(),put.getRequestCharSet()));
  898. bos.close();
  899. }
  900. else {
  901. putBody.append("<RequestEntity was not repeatable, cannot view what was sent>");
  902. }
  903. if(!hasContentTypeHeader) {
  904. // Allow the mimetype of the file to control the content type
  905. // This is not obvious in GUI if you are not uploading any files,
  906. // but just sending the content of nameless parameters
  907. // TODO: needs a multiple file upload scenerio
  908. HTTPFileArg file = files.length > 0? files[0] : null;
  909. if(file != null && file.getMimeType() != null && file.getMimeType().length() > 0) {
  910. put.setRequestHeader(HEADER_CONTENT_TYPE, file.getMimeType());
  911. }
  912. }
  913. // Set the content length
  914. put.setRequestHeader(HEADER_CONTENT_LENGTH, Long.toString(put.getRequestEntity().getContentLength()));
  915. return putBody.toString();
  916. }
  917. return null;
  918. }
  919. /**
  920. * Class extending FilePart, so that we can send placeholder text
  921. * instead of the actual file content
  922. */
  923. private static class ViewableFilePart extends FilePart {
  924. private boolean hideFileData;
  925. public ViewableFilePart(String name, File file, String contentType, String charset) throws FileNotFoundException {
  926. super(name, file, contentType, charset);
  927. this.hideFileData = false;
  928. }
  929. public void setHideFileData(boolean hideFileData) {
  930. this.hideFileData = hideFileData;
  931. }
  932. @Override
  933. protected void sendData(OutputStream out) throws IOException {
  934. // Check if we should send only placeholder text for the
  935. // file content, or the real file content
  936. if(hideFileData) {
  937. out.write("<actual file content, not shown here>".getBytes());// encoding does not really matter here
  938. }
  939. else {
  940. super.sendData(out);
  941. }
  942. }
  943. }
  944. /**
  945. * From the <code>HttpMethod</code>, store all the "set-cookie" key-pair
  946. * values in the cookieManager of the <code>UrlConfig</code>.
  947. *
  948. * @param method
  949. * <code>HttpMethod</code> which represents the request
  950. * @param u
  951. * <code>URL</code> of the URL request
  952. * @param cookieManager
  953. * the <code>CookieManager</code> containing all the cookies
  954. */
  955. protected void saveConnectionCookies(HttpMethod method, URL u, CookieManager cookieManager) {
  956. if (cookieManager != null) {
  957. Header hdr[] = method.getResponseHeaders(HEADER_SET_COOKIE);
  958. for (int i = 0; i < hdr.length; i++) {
  959. cookieManager.addCookieFromHeader(hdr[i].getValue(),u);
  960. }
  961. }
  962. }
  963. @Override
  964. public void threadFinished() {
  965. log.debug("Thread Finished");
  966. closeThreadLocalConnections();
  967. }
  968. /**
  969. *
  970. */
  971. private void closeThreadLocalConnections() {
  972. // Does not need to be synchronised, as all access is from same thread
  973. Map<HostConfiguration, HttpClient> map = httpClients.get();
  974. if ( map != null ) {
  975. for (HttpClient cl : map.values())
  976. {
  977. // Can cause NPE in HttpClient 3.1
  978. //((SimpleHttpConnectionManager)cl.getHttpConnectionManager()).shutdown();// Closes the connection
  979. // Revert to original method:
  980. cl.getHttpConnectionManager().closeIdleConnections(-1000);// Closes the connection
  981. }
  982. map.clear();
  983. }
  984. }
  985. /** {@inheritDoc} */
  986. public boolean interrupt() {
  987. HttpClient client = savedClient;
  988. if (client != null) {
  989. savedClient = null;
  990. // TODO - not sure this is the best method
  991. final HttpConnectionManager httpConnectionManager = client.getHttpConnectionManager();
  992. if (httpConnectionManager instanceof SimpleHttpConnectionManager) {// Should be true
  993. ((SimpleHttpConnectionManager)httpConnectionManager).shutdown();
  994. }
  995. }
  996. return client != null;
  997. }
  998. /**
  999. * {@inheritDoc}
  1000. * This implementation closes all local connections.
  1001. */
  1002. @Override
  1003. protected void notifySSLContextWasReset() {
  1004. log.debug("freeThreadConnections called");
  1005. closeThreadLocalConnections();
  1006. }
  1007. }