diff --git a/bin/jmeter.properties b/bin/jmeter.properties index dd4dd7eaacd..03d215c40ce 100644 --- a/bin/jmeter.properties +++ b/bin/jmeter.properties @@ -378,8 +378,92 @@ remote_hosts=127.0.0.1 #httpclient.timeout=0 # 0 == no timeout -# Set the http version (defaults to 1.1) -#httpclient.version=1.1 (or use the parameter http.protocol.version) +# Set the default HTTP version for HttpClient5 and Java samplers when HTTP Version is empty +# Valid values are HTTP/1.1, HTTP/2 and, for HttpClient5 only, HTTP/2 Strict (defaults to HTTP/1.1) +#httpclient.version=HTTP/1.1 + +# Milliseconds of inactivity after which a pooled connection of the HttpClient5 +# sampler implementation is re-validated before it is reused (HTTP/2 PING, or a +# stale check for HTTP/1.1), -1 disables the check. Without it a connection the +# server closed while the thread was idle is reused as it is and fails the +# sample, as JMeter deliberately does not retry requests +#httpclient5.validate_after_inactivity=2000 + +# HTTP/2 settings used by the HttpClient5 sampler implementation + +# Multiplex concurrent message exchanges (e.g. parallel downloads of embedded +# resources) over a single HTTP/2 connection +#httpclient5.h2.multiplexing=true + +# Maximum number of HTTP/2 connections kept per target host. With multiplexing +# enabled a single connection usually serves all concurrent requests +#httpclient5.h2.max_connections_per_route=6 + +# Size in bytes of the HPACK dynamic header table announced to the server. +# 0 disables indexing of headers by the server +#httpclient5.h2.header_table_size=8192 + +# Compress the request headers with HPACK +#httpclient5.h2.header_compression=true + +# Maximum number of concurrent streams the client accepts on a connection +#httpclient5.h2.max_concurrent_streams=250 + +# Flow control window in bytes announced per stream +#httpclient5.h2.initial_window_size=65535 + +# Largest frame payload in bytes the client is willing to receive +#httpclient5.h2.max_frame_size=65536 + +# Accept server push. JMeter cannot report pushed resources, so they are +# dropped, therefore push is disabled by default +#httpclient5.h2.push_enabled=false + +# Use HTTP/2 over cleartext (h2c, prior knowledge) when HTTP/2 is selected for +# a http:// URL. Without it such requests fall back to HTTP/1.1, because +# protocol negotiation requires TLS. Only enable it if all plain HTTP targets +# support h2c +#httpclient5.h2.prior_knowledge=false + +# HTTP/2 settings used by the Java sampler implementation. Except for the +# multiplexing they are applied as jdk.httpclient.* system properties, so a +# setting given on the command line always takes precedence + +# Multiplex concurrent message exchanges (e.g. requests of different threads or +# parallel downloads of embedded resources) over a single HTTP/2 connection. +# Requests which start before the first HTTP/2 connection to a host has been +# established still open a connection of their own +#http.java.h2.multiplexing=true + +# Size in bytes of the HPACK dynamic header table announced to the server. +# 0 disables indexing of headers by the server (JDK default: 16384) +#http.java.h2.header_table_size=16384 + +# Maximum number of server initiated (pushed) streams the client accepts on a +# connection (JDK default: 100 with server push enabled, 0 otherwise) +#http.java.h2.max_concurrent_streams=100 + +# Flow control window in bytes announced per stream (JDK default: 16777216) +#http.java.h2.initial_window_size=16777216 + +# Flow control window in bytes announced for the whole connection, it must not +# be smaller than the stream window (JDK default: 67108864) +#http.java.h2.connection_window_size=67108864 + +# Largest frame payload in bytes the client is willing to receive, between +# 16384 and 16777215 (JDK default: 16384) +#http.java.h2.max_frame_size=16384 + +# Seconds an idle HTTP/2 connection is kept in the pool (JDK default: the value +# of jdk.httpclient.keepalive.timeout, which defaults to 30) +#http.java.h2.keep_alive_timeout=30 + +# Accept server push. JMeter cannot report pushed resources, so they are +# dropped, therefore push is disabled by default +#http.java.h2.push_enabled=false + +# If true, default HC5 User-Agent will not be added +#httpclient5.default_user_agent_disabled=false # Define characters per second > 0 to emulate slow connections #httpclient.socket.http.cps=0 diff --git a/src/bom-thirdparty/build.gradle.kts b/src/bom-thirdparty/build.gradle.kts index ffd0b4073f5..62bca7e461c 100644 --- a/src/bom-thirdparty/build.gradle.kts +++ b/src/bom-thirdparty/build.gradle.kts @@ -106,7 +106,9 @@ dependencies { api("org.apache.commons:commons-text:1.14.0") { because("User might still rely on commons-text") } - api("org.apache.httpcomponents.client5:httpclient5:5.5.1") + api("org.apache.httpcomponents.client5:httpclient5:5.6.4") + api("org.apache.httpcomponents.core5:httpcore5:5.4.3") + api("org.apache.httpcomponents.core5:httpcore5-h2:5.4.3") api("org.apache.httpcomponents:httpasyncclient:4.1.5") api("org.apache.httpcomponents:httpclient:4.5.14") api("org.apache.httpcomponents:httpcore-nio:4.4.16") diff --git a/src/core/src/main/resources/org/apache/jmeter/resources/messages.properties b/src/core/src/main/resources/org/apache/jmeter/resources/messages.properties index 3137d879e74..3ba59ea9f1f 100644 --- a/src/core/src/main/resources/org/apache/jmeter/resources/messages.properties +++ b/src/core/src/main/resources/org/apache/jmeter/resources/messages.properties @@ -473,6 +473,7 @@ html_assertion_title=HTML Assertion html_extractor_title=CSS Selector Extractor html_extractor_type=CSS Selector Extractor Implementation http_implementation=Implementation: +http_version=HTTP Version: html_report=Generate HTML report http_response_code=HTTP response code http_url_rewriting_modifier_title=HTTP URL Re-writing Modifier diff --git a/src/core/src/main/resources/org/apache/jmeter/resources/messages_de.properties b/src/core/src/main/resources/org/apache/jmeter/resources/messages_de.properties index f1278670119..17ff43ab91f 100644 --- a/src/core/src/main/resources/org/apache/jmeter/resources/messages_de.properties +++ b/src/core/src/main/resources/org/apache/jmeter/resources/messages_de.properties @@ -16,6 +16,7 @@ # about=Über Apache JMeter +http_version=HTTP-Version\: add=Hinzufügen add_as_child=Als ein Kind hinzufügen add_parameter=Variable hinzufügen diff --git a/src/core/src/main/resources/org/apache/jmeter/resources/messages_es.properties b/src/core/src/main/resources/org/apache/jmeter/resources/messages_es.properties index 8fe4fb5a53d..dcb50db6a35 100644 --- a/src/core/src/main/resources/org/apache/jmeter/resources/messages_es.properties +++ b/src/core/src/main/resources/org/apache/jmeter/resources/messages_es.properties @@ -273,6 +273,7 @@ html_assertion_file=Escribir el reporte JTidy en fichero html_assertion_label=Aserción HTML html_assertion_title=Aserción HTML http_implementation=Implementación HTTP\: +http_version=Versión HTTP\: http_response_code=código de respuesta HTTP http_url_rewriting_modifier_title=Modificador de re-escritura HTTP URL http_user_parameter_modifier=Modificador de Parámetro de Usuario HTTP diff --git a/src/core/src/main/resources/org/apache/jmeter/resources/messages_fr.properties b/src/core/src/main/resources/org/apache/jmeter/resources/messages_fr.properties index 044d7ba8351..209f0a84645 100644 --- a/src/core/src/main/resources/org/apache/jmeter/resources/messages_fr.properties +++ b/src/core/src/main/resources/org/apache/jmeter/resources/messages_fr.properties @@ -467,6 +467,7 @@ html_assertion_title=Assertion HTML html_extractor_title=Extracteur par Sélecteur CSS html_extractor_type=Implémentation de l'extracteur Sélecteur CSS http_implementation=Implémentation \: +http_version=Version HTTP \: html_report=Générer le rapport HTML http_response_code=Code de réponse HTTP http_url_rewriting_modifier_title=Transcripteur d'URL HTTP diff --git a/src/core/src/main/resources/org/apache/jmeter/resources/messages_ja.properties b/src/core/src/main/resources/org/apache/jmeter/resources/messages_ja.properties index 561489d9e29..6fe16f42798 100644 --- a/src/core/src/main/resources/org/apache/jmeter/resources/messages_ja.properties +++ b/src/core/src/main/resources/org/apache/jmeter/resources/messages_ja.properties @@ -176,6 +176,7 @@ grouping_store_first_only=各グループの最初のサンプラーだけ保存 header_manager_title=HTTP ヘッダマネージャ headers_stored=ヘッダーマネージャに保存されているヘッダ help=ヘルプ +http_version=HTTP バージョン\: http_response_code=HTTP応答コード http_url_rewriting_modifier_title=HTTP URL-Rewriting 修飾子 http_user_parameter_modifier=HTTPユーザーパラメータの変更 diff --git a/src/core/src/main/resources/org/apache/jmeter/resources/messages_ko.properties b/src/core/src/main/resources/org/apache/jmeter/resources/messages_ko.properties index 8b963980d24..1de63f00cd6 100644 --- a/src/core/src/main/resources/org/apache/jmeter/resources/messages_ko.properties +++ b/src/core/src/main/resources/org/apache/jmeter/resources/messages_ko.properties @@ -471,6 +471,7 @@ html_assertion_title=HTML Assertion html_extractor_title=CSS Selector 추출기 html_extractor_type=CSS Selector 추출기 구현 http_implementation=구현\: +http_version=HTTP 버전\: html_report=HTML 보고서 생성 http_response_code=HTTP 응답 코드 http_url_rewriting_modifier_title=HTTP URL Re-writing Modifier diff --git a/src/core/src/main/resources/org/apache/jmeter/resources/messages_no.properties b/src/core/src/main/resources/org/apache/jmeter/resources/messages_no.properties index a6daacc3059..160b0288aa1 100644 --- a/src/core/src/main/resources/org/apache/jmeter/resources/messages_no.properties +++ b/src/core/src/main/resources/org/apache/jmeter/resources/messages_no.properties @@ -71,6 +71,7 @@ graph_results_deviation=Avvik graph_results_title=Graf resultater headers_stored=Headere lagret hos headermanager help=Hjelp +http_version=HTTP-versjon\: infinite=Uendelig interleave_control_title=Vekslende kontroller iterator_num=Løkketeller\: diff --git a/src/core/src/main/resources/org/apache/jmeter/resources/messages_pl.properties b/src/core/src/main/resources/org/apache/jmeter/resources/messages_pl.properties index fb5aedd2f24..9d81a10d112 100644 --- a/src/core/src/main/resources/org/apache/jmeter/resources/messages_pl.properties +++ b/src/core/src/main/resources/org/apache/jmeter/resources/messages_pl.properties @@ -16,6 +16,7 @@ # about=O programie Apache JMeter +http_version=Wersja HTTP\: action_check_message=Aktualnie jest przeprowadzany test, zatrzymaj albo wyłącz test, aby uruchomić to polecenie action_check_title=Test uruchomiony active_total_threads_tooltip=Uruchamianie wątków / całkowita liczba wątków do uruchomienia diff --git a/src/core/src/main/resources/org/apache/jmeter/resources/messages_pt_BR.properties b/src/core/src/main/resources/org/apache/jmeter/resources/messages_pt_BR.properties index e9960bf8719..e278975db23 100644 --- a/src/core/src/main/resources/org/apache/jmeter/resources/messages_pt_BR.properties +++ b/src/core/src/main/resources/org/apache/jmeter/resources/messages_pt_BR.properties @@ -261,6 +261,7 @@ html_assertion_file=Escrever relatório do JTidy em arquivo html_assertion_label=Asserção HTML html_assertion_title=Asserção HTML http_implementation=Implementação\: +http_version=Versão HTTP\: http_response_code=Código da Resposta HTTP http_url_rewriting_modifier_title=Modificador de Re-escrita de URL HTTP http_user_parameter_modifier=Modificador de Parâmetros HTTP do Usuário diff --git a/src/core/src/main/resources/org/apache/jmeter/resources/messages_tr.properties b/src/core/src/main/resources/org/apache/jmeter/resources/messages_tr.properties index 352f8bd7e7a..a18b1fffbf4 100644 --- a/src/core/src/main/resources/org/apache/jmeter/resources/messages_tr.properties +++ b/src/core/src/main/resources/org/apache/jmeter/resources/messages_tr.properties @@ -246,6 +246,7 @@ html_assertion_file=JTidy raporunu dosyaya yaz html_assertion_label=HTML Doğrulama html_assertion_title=HTML Doğrulama http_implementation=Uygulaması\: +http_version=HTTP sürümü\: http_response_code=HTTP cevap kodu http_url_rewriting_modifier_title=HTTP URL Yeniden Yazma Niteleyicisi http_user_parameter_modifier=HTTP Kullanıcı Parametresi Niteleyicisi diff --git a/src/core/src/main/resources/org/apache/jmeter/resources/messages_zh_CN.properties b/src/core/src/main/resources/org/apache/jmeter/resources/messages_zh_CN.properties index 1763f2be645..253e35f83ed 100644 --- a/src/core/src/main/resources/org/apache/jmeter/resources/messages_zh_CN.properties +++ b/src/core/src/main/resources/org/apache/jmeter/resources/messages_zh_CN.properties @@ -433,6 +433,7 @@ html_assertion_title=HTML断言 html_extractor_title=CSS/JQuery提取器 html_extractor_type=CSS 选择器提取器实现 http_implementation=实现: +http_version=HTTP 版本: http_response_code=HTTP响应代码 http_url_rewriting_modifier_title=HTTP URL 重写修饰符 http_user_parameter_modifier=HTTP 用户参数修饰符 diff --git a/src/core/src/main/resources/org/apache/jmeter/resources/messages_zh_TW.properties b/src/core/src/main/resources/org/apache/jmeter/resources/messages_zh_TW.properties index 93107f3298c..49012192320 100644 --- a/src/core/src/main/resources/org/apache/jmeter/resources/messages_zh_TW.properties +++ b/src/core/src/main/resources/org/apache/jmeter/resources/messages_zh_TW.properties @@ -206,6 +206,7 @@ headers_stored=標頭管理員中儲存的標頭資料 help=輔助說明 html_assertion_label=HTML 驗證 html_assertion_title=HTML 驗證 +http_version=HTTP 版本: http_response_code=HTTP 回應代碼 http_url_rewriting_modifier_title=HTTP URL 重導修飾詞 http_user_parameter_modifier=HTTP 使用者參數修飾詞 diff --git a/src/dist-check/src/test/java/org/apache/jmeter/junit/JMeterTest.java b/src/dist-check/src/test/java/org/apache/jmeter/junit/JMeterTest.java index 2aca34f3e1e..0aff1b1a678 100644 --- a/src/dist-check/src/test/java/org/apache/jmeter/junit/JMeterTest.java +++ b/src/dist-check/src/test/java/org/apache/jmeter/junit/JMeterTest.java @@ -385,6 +385,7 @@ public void GUIComponents2(GuiComponentHolder componentHolder) throws Exception // TODO: support expressions? IGNORED_PROPERTIES.add(HTTPSamplerBaseSchema.INSTANCE.getIpSourceType()); IGNORED_PROPERTIES.add(HTTPSamplerBaseSchema.INSTANCE.getImplementation()); + IGNORED_PROPERTIES.add(HTTPSamplerBaseSchema.INSTANCE.getHttpVersion()); // TODO: support expressions in UrlConfigGui IGNORED_PROPERTIES.add(HTTPSamplerBaseSchema.INSTANCE.getFollowRedirects()); IGNORED_PROPERTIES.add(HTTPSamplerBaseSchema.INSTANCE.getAutoRedirects()); diff --git a/src/protocol/http/build.gradle.kts b/src/protocol/http/build.gradle.kts index af6b3482f5d..d8d5c20017e 100644 --- a/src/protocol/http/build.gradle.kts +++ b/src/protocol/http/build.gradle.kts @@ -61,6 +61,11 @@ dependencies { exclude("com.google.code.findbugs", "jsr305") } implementation("dnsjava:dnsjava") + implementation("org.apache.httpcomponents.client5:httpclient5") + implementation("org.apache.httpcomponents.core5:httpcore5") + implementation("org.apache.httpcomponents.core5:httpcore5-h2") { + because("HTTPHC5Impl uses H2Config and HttpVersionPolicy from org.apache.hc.core5.http2") + } implementation("org.apache.httpcomponents:httpmime") implementation("org.apache.httpcomponents:httpcore") implementation("org.brotli:dec") diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/HttpDefaultsGui.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/HttpDefaultsGui.java index 8b49bcbb4ec..71bf2ca5e2a 100644 --- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/HttpDefaultsGui.java +++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/config/gui/HttpDefaultsGui.java @@ -37,6 +37,7 @@ import org.apache.jmeter.gui.TestElementMetadata; import org.apache.jmeter.gui.util.HorizontalPanel; import org.apache.jmeter.gui.util.VerticalPanel; +import org.apache.jmeter.protocol.http.gui.HttpVersionComboBox; import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase; import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBaseSchema; import org.apache.jmeter.protocol.http.sampler.HTTPSamplerFactory; @@ -79,6 +80,7 @@ public class HttpDefaultsGui extends AbstractConfigGui { private JTextField proxyUser; private JPasswordField proxyPass; private final JComboBox httpImplementation = new JComboBox<>(HTTPSamplerFactory.getImplementations()); + private final HttpVersionComboBox httpVersion = new HttpVersionComboBox(); private JTextField connectTimeOut; private JTextField responseTimeOut; @@ -152,6 +154,7 @@ public void modifyTestElement(TestElement config) { } config.set(httpSchema.getImplementation(), String.valueOf(httpImplementation.getSelectedItem())); + config.set(httpSchema.getHttpVersion(), String.valueOf(httpVersion.getSelectedItem())); } @Override @@ -169,6 +172,8 @@ public void configure(TestElement el) { HTTPSamplerBaseSchema httpSchema = HTTPSamplerBaseSchema.INSTANCE; sourceIpType.setSelectedIndex(samplerBase.get(httpSchema.getIpSourceType())); httpImplementation.setSelectedItem(samplerBase.getString(httpSchema.getImplementation())); + httpVersion.setImplementation(samplerBase.getString(httpSchema.getImplementation())); + httpVersion.setSelectedItem(samplerBase.getString(httpSchema.getHttpVersion())); } private void init() { // WARNING: called from ctor so must not be overridden (i.e. must be private or final) @@ -329,6 +334,9 @@ protected final JPanel getImplementationPanel(){ implPanel.add(new JLabel(JMeterUtils.getResString("http_implementation"))); // $NON-NLS-1$ httpImplementation.addItem("");// $NON-NLS-1$ implPanel.add(httpImplementation); + implPanel.add(new JLabel(JMeterUtils.getResString("http_version"))); // $NON-NLS-1$ + httpVersion.bindToImplementation(httpImplementation); + implPanel.add(httpVersion); return implPanel; } diff --git a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/control/CacheManager.java b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/control/CacheManager.java index 8852f6332e4..32509ab81f8 100644 --- a/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/control/CacheManager.java +++ b/src/protocol/http/src/main/java/org/apache/jmeter/protocol/http/control/CacheManager.java @@ -245,6 +245,55 @@ public void saveDetails(HttpResponse method, HTTPSampleResult res) { } } + /** + * Save the Last-Modified, Etag, and Expires headers if the result is + * cacheable. Version for Apache HttpClient 5 implementation. + * + * @param response response to extract header information from + * @param res result to decide if result is cacheable + */ + public void saveDetails(org.apache.hc.core5.http.ClassicHttpResponse response, HTTPSampleResult res) { + final String varyHeader = getHeader(response, HTTPConstants.VARY); + if (isCacheable(res, varyHeader)) { + String lastModified = getHeader(response, HTTPConstants.LAST_MODIFIED); + String expires = getHeader(response, HTTPConstants.EXPIRES); + String etag = getHeader(response, HTTPConstants.ETAG); + String cacheControl = getHeader(response, HTTPConstants.CACHE_CONTROL); + String date = getHeader(response, HTTPConstants.DATE); + if (anyNotBlank(lastModified, expires, etag, cacheControl)) { + setCache(lastModified, cacheControl, expires, etag, + res.getUrlAsString(), date, getVaryHeader(varyHeader, asHeaders(res.getRequestHeaders()))); + } + } + } + + /** + * Save the Last-Modified, Etag, and Expires headers if the result is + * cacheable. Version for Java HttpClient implementation. + * + * @param response response to extract header information from + * @param res result to decide if result is cacheable + */ + public void saveDetails(java.net.http.HttpResponse response, HTTPSampleResult res) { + final String varyHeader = response.headers().firstValue(HTTPConstants.VARY).orElse(null); + if (isCacheable(res, varyHeader)) { + String lastModified = response.headers().firstValue(HTTPConstants.LAST_MODIFIED).orElse(null); + String expires = response.headers().firstValue(HTTPConstants.EXPIRES).orElse(null); + String etag = response.headers().firstValue(HTTPConstants.ETAG).orElse(null); + String cacheControl = response.headers().firstValue(HTTPConstants.CACHE_CONTROL).orElse(null); + String date = response.headers().firstValue(HTTPConstants.DATE).orElse(null); + if (anyNotBlank(lastModified, expires, etag, cacheControl)) { + setCache(lastModified, cacheControl, expires, etag, + res.getUrlAsString(), date, getVaryHeader(varyHeader, asHeaders(res.getRequestHeaders()))); + } + } + } + + private static String getHeader(org.apache.hc.core5.http.HttpMessage message, String name) { + org.apache.hc.core5.http.Header header = message.getFirstHeader(name); + return header == null ? null : header.getValue(); + } + // helper method to save the cache entry private void setCache(String lastModified, String cacheControl, String expires, String etag, String url, String date, Map.Entry varyHeader) { @@ -410,6 +459,29 @@ public void setHeaders(URL url, HttpRequestBase request) { } } + /** + * Check the cache, and if there is a match, set conditional request headers. + * + * @param url URL to look up in cache + * @param request request where to set the headers + */ + public void setHeaders(URL url, org.apache.hc.core5.http.ClassicHttpRequest request) { + CacheEntry entry = getEntry(url.toString(), asHeaders(request.getHeaders())); + if (log.isDebugEnabled()) { + log.debug("setHeaders for HTTP Method:{}(HC5) URL:{} Entry:{}", request.getMethod(), url, entry); + } + if (entry != null) { + String lastModified = entry.getLastModified(); + if (lastModified != null) { + request.setHeader(HTTPConstants.IF_MODIFIED_SINCE, lastModified); + } + String etag = entry.getEtag(); + if (etag != null) { + request.setHeader(HTTPConstants.IF_NONE_MATCH, etag); + } + } + } + /** * Check the cache, and if there is a match, set the headers: * +

HTTP/2 support for the HttpClient5 and Java sampler implementations:

+ +

Timers, Assertions, Config, Pre- & Post-Processors

UI

@@ -116,6 +128,7 @@ Summary
  • 6620Fix report generation paths so dashboard output files are created in the correct location after internal refactoring.
  • 6456Handle malformed percent-encoded URLs gracefully when recording HTTP traffic, logging a warning instead of failing the recording.
  • + Thanks @@ -127,6 +140,7 @@ Summary
  • Gabriele Coletta (github.com/gdmg92)
  • Patrick Uiterwijk (patrick at puiterwijk.org)
  • Piotr P. Karwasz (github.com/piotrgithub)
  • +
  • Andreas Lind (github.com/andreaslind01)
  • We also thank bug reporters who helped us improve JMeter.