Browse Source

Merge remote-tracking branch 'es/master' into enrich

Martijn van Groningen 6 years ago
parent
commit
c8436a7a36
100 changed files with 3957 additions and 1435 deletions
  1. 1 1
      build.gradle
  2. 13 10
      buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy
  3. 2 2
      buildSrc/src/main/groovy/org/elasticsearch/gradle/doc/SnippetsTask.groovy
  4. 1 1
      buildSrc/src/main/groovy/org/elasticsearch/gradle/test/ClusterFormationTasks.groovy
  5. 24 0
      buildSrc/src/test/groovy/org/elasticsearch/gradle/BuildPluginTests.java
  6. 55 0
      buildSrc/src/test/java/org/elasticsearch/gradle/BuildPluginIT.java
  7. 9 0
      client/rest-high-level/build.gradle
  8. 53 53
      client/rest-high-level/src/main/java/org/elasticsearch/client/CcrClient.java
  9. 14 8
      client/rest-high-level/src/main/java/org/elasticsearch/client/ClusterClient.java
  10. 33 33
      client/rest-high-level/src/main/java/org/elasticsearch/client/DataFrameClient.java
  11. 8 7
      client/rest-high-level/src/main/java/org/elasticsearch/client/GraphClient.java
  12. 56 38
      client/rest-high-level/src/main/java/org/elasticsearch/client/IndexLifecycleClient.java
  13. 200 133
      client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java
  14. 16 10
      client/rest-high-level/src/main/java/org/elasticsearch/client/IngestClient.java
  15. 20 13
      client/rest-high-level/src/main/java/org/elasticsearch/client/LicenseClient.java
  16. 197 137
      client/rest-high-level/src/main/java/org/elasticsearch/client/MachineLearningClient.java
  17. 4 3
      client/rest-high-level/src/main/java/org/elasticsearch/client/MigrationClient.java
  18. 177 131
      client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java
  19. 32 26
      client/rest-high-level/src/main/java/org/elasticsearch/client/RollupClient.java
  20. 157 84
      client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityClient.java
  21. 8 1
      client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityRequestConverters.java
  22. 51 31
      client/rest-high-level/src/main/java/org/elasticsearch/client/SnapshotClient.java
  23. 17 12
      client/rest-high-level/src/main/java/org/elasticsearch/client/TasksClient.java
  24. 38 25
      client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherClient.java
  25. 7 5
      client/rest-high-level/src/main/java/org/elasticsearch/client/XPackClient.java
  26. 13 11
      client/rest-high-level/src/main/java/org/elasticsearch/client/ml/dataframe/DataFrameAnalyticsStats.java
  27. 5 1
      client/rest-high-level/src/main/java/org/elasticsearch/client/ml/dataframe/MlDataFrameAnalysisNamedXContentProvider.java
  28. 91 0
      client/rest-high-level/src/main/java/org/elasticsearch/client/ml/dataframe/PhaseProgress.java
  29. 242 0
      client/rest-high-level/src/main/java/org/elasticsearch/client/ml/dataframe/Regression.java
  30. 107 0
      client/rest-high-level/src/main/java/org/elasticsearch/client/security/DelegatePkiAuthenticationRequest.java
  31. 88 0
      client/rest-high-level/src/main/java/org/elasticsearch/client/security/DelegatePkiAuthenticationResponse.java
  32. 32 10
      client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetApiKeyRequest.java
  33. 33 10
      client/rest-high-level/src/main/java/org/elasticsearch/client/security/InvalidateApiKeyRequest.java
  34. 45 3
      client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningIT.java
  35. 4 4
      client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java
  36. 21 4
      client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityRequestConvertersTests.java
  37. 17 4
      client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MlClientDocumentationIT.java
  38. 131 6
      client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java
  39. 14 3
      client/rest-high-level/src/test/java/org/elasticsearch/client/ml/dataframe/DataFrameAnalyticsStatsTests.java
  40. 46 0
      client/rest-high-level/src/test/java/org/elasticsearch/client/ml/dataframe/PhaseProgressTests.java
  41. 54 0
      client/rest-high-level/src/test/java/org/elasticsearch/client/ml/dataframe/RegressionTests.java
  42. 107 0
      client/rest-high-level/src/test/java/org/elasticsearch/client/security/DelegatePkiAuthenticationRequestTests.java
  43. 53 0
      client/rest-high-level/src/test/java/org/elasticsearch/client/security/DelegatePkiAuthenticationResponseTests.java
  44. 23 11
      client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetApiKeyRequestTests.java
  45. 23 11
      client/rest-high-level/src/test/java/org/elasticsearch/client/security/InvalidateApiKeyRequestTests.java
  46. 35 0
      client/rest-high-level/src/test/resources/org/elasticsearch/client/security/delegate_pki/README.asciidoc
  47. 185 0
      client/rest-high-level/src/test/resources/org/elasticsearch/client/security/delegate_pki/openssl_config.cnf
  48. 21 0
      client/rest-high-level/src/test/resources/org/elasticsearch/client/security/delegate_pki/testClient.crt
  49. 27 0
      client/rest-high-level/src/test/resources/org/elasticsearch/client/security/delegate_pki/testClient.key
  50. 24 0
      client/rest-high-level/src/test/resources/org/elasticsearch/client/security/delegate_pki/testIntermediateCA.crt
  51. 27 0
      client/rest-high-level/src/test/resources/org/elasticsearch/client/security/delegate_pki/testIntermediateCA.key
  52. 24 0
      client/rest-high-level/src/test/resources/org/elasticsearch/client/security/delegate_pki/testRootCA.crt
  53. 27 0
      client/rest-high-level/src/test/resources/org/elasticsearch/client/security/delegate_pki/testRootCA.key
  54. 1 1
      dev-tools/smoke_test_rc.py
  55. 1 0
      distribution/build.gradle
  56. 1 1
      distribution/docker/src/docker/bin/docker-entrypoint.sh
  57. 36 0
      docs/build.gradle
  58. 24 4
      docs/java-rest/high-level/ml/put-data-frame-analytics.asciidoc
  59. 62 0
      docs/java-rest/high-level/security/delegate-pki-authentication.asciidoc
  60. 8 0
      docs/java-rest/high-level/security/get-api-key.asciidoc
  61. 8 0
      docs/java-rest/high-level/security/invalidate-api-key.asciidoc
  62. 235 0
      docs/reference/aggregations/pipeline/cumulative-cardinality-aggregation.asciidoc
  63. 1 1
      docs/reference/cat/alias.asciidoc
  64. 1 1
      docs/reference/cat/allocation.asciidoc
  65. 1 1
      docs/reference/cat/count.asciidoc
  66. 2 2
      docs/reference/cat/fielddata.asciidoc
  67. 1 1
      docs/reference/cat/indices.asciidoc
  68. 1 1
      docs/reference/cat/recovery.asciidoc
  69. 1 1
      docs/reference/cat/segments.asciidoc
  70. 1 1
      docs/reference/cat/shards.asciidoc
  71. 2 2
      docs/reference/cat/snapshots.asciidoc
  72. 2 2
      docs/reference/cat/templates.asciidoc
  73. 2 2
      docs/reference/cat/thread_pool.asciidoc
  74. 1 1
      docs/reference/cluster/health.asciidoc
  75. 1 1
      docs/reference/cluster/nodes-hot-threads.asciidoc
  76. 4 4
      docs/reference/cluster/nodes-info.asciidoc
  77. 7 7
      docs/reference/cluster/nodes-stats.asciidoc
  78. 4 4
      docs/reference/cluster/nodes-usage.asciidoc
  79. 2 2
      docs/reference/cluster/state.asciidoc
  80. 1 1
      docs/reference/cluster/stats.asciidoc
  81. 2 2
      docs/reference/cluster/tasks.asciidoc
  82. 2 2
      docs/reference/cluster/voting-exclusions.asciidoc
  83. 3 3
      docs/reference/docs/get.asciidoc
  84. 106 340
      docs/reference/getting-started.asciidoc
  85. 3 0
      docs/reference/indices.asciidoc
  86. 85 0
      docs/reference/indices/close.asciidoc
  87. 78 27
      docs/reference/indices/create-index.asciidoc
  88. 42 10
      docs/reference/indices/delete-index.asciidoc
  89. 86 38
      docs/reference/indices/get-field-mapping.asciidoc
  90. 46 8
      docs/reference/indices/get-index.asciidoc
  91. 1 1
      docs/reference/indices/get-mapping.asciidoc
  92. 53 11
      docs/reference/indices/get-settings.asciidoc
  93. 48 6
      docs/reference/indices/indices-exists.asciidoc
  94. 78 48
      docs/reference/indices/open-close.asciidoc
  95. 104 23
      docs/reference/indices/put-mapping.asciidoc
  96. 63 16
      docs/reference/indices/update-settings.asciidoc
  97. 15 0
      docs/reference/ingest/apis/index.asciidoc
  98. 0 15
      docs/reference/ingest/ingest-node.asciidoc
  99. 1 0
      docs/reference/mapping.asciidoc
  100. 18 2
      docs/reference/migration/migrate_8_0/settings.asciidoc

+ 1 - 1
build.gradle

@@ -33,7 +33,7 @@ import static org.elasticsearch.gradle.tool.Boilerplate.maybeConfigure
 
 plugins {
     id 'com.gradle.build-scan' version '2.4'
-    id 'base'
+    id 'lifecycle-base'
     id 'elasticsearch.global-build-info'
 }
 

+ 13 - 10
buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy

@@ -48,7 +48,6 @@ import org.gradle.api.artifacts.ModuleVersionIdentifier
 import org.gradle.api.artifacts.ProjectDependency
 import org.gradle.api.artifacts.ResolvedArtifact
 import org.gradle.api.artifacts.dsl.RepositoryHandler
-import org.gradle.api.artifacts.repositories.ArtifactRepository
 import org.gradle.api.artifacts.repositories.IvyArtifactRepository
 import org.gradle.api.artifacts.repositories.IvyPatternRepositoryLayout
 import org.gradle.api.artifacts.repositories.MavenArtifactRepository
@@ -85,12 +84,10 @@ import org.gradle.util.GradleVersion
 
 import java.nio.charset.StandardCharsets
 import java.nio.file.Files
-import java.time.ZoneOffset
-import java.time.ZonedDateTime
 import java.util.regex.Matcher
 
-import static org.elasticsearch.gradle.tool.Boilerplate.maybeConfigure
 import static org.elasticsearch.gradle.tool.Boilerplate.findByName
+import static org.elasticsearch.gradle.tool.Boilerplate.maybeConfigure
 
 /**
  * Encapsulates build configuration for elasticsearch projects.
@@ -411,11 +408,11 @@ class BuildPlugin implements Plugin<Project> {
         project.getRepositories().all { repository ->
             if (repository instanceof MavenArtifactRepository) {
                 final MavenArtifactRepository maven = (MavenArtifactRepository) repository
-                assertRepositoryURIUsesHttps(maven, project, maven.getUrl())
-                repository.getArtifactUrls().each { uri -> assertRepositoryURIUsesHttps(maven, project, uri) }
+                assertRepositoryURIIsSecure(maven.name, project.path, maven.getUrl())
+                repository.getArtifactUrls().each { uri -> assertRepositoryURIIsSecure(maven.name, project.path, uri) }
             } else if (repository instanceof IvyArtifactRepository) {
                 final IvyArtifactRepository ivy = (IvyArtifactRepository) repository
-                assertRepositoryURIUsesHttps(ivy, project, ivy.getUrl())
+                assertRepositoryURIIsSecure(ivy.name, project.path, ivy.getUrl())
             }
         }
         RepositoryHandler repos = project.repositories
@@ -455,9 +452,15 @@ class BuildPlugin implements Plugin<Project> {
         }
     }
 
-    private static void assertRepositoryURIUsesHttps(final ArtifactRepository repository, final Project project, final URI uri) {
-        if (uri != null && uri.toURL().getProtocol().equals("http")) {
-            throw new GradleException("repository [${repository.name}] on project with path [${project.path}] is using http for artifacts on [${uri.toURL()}]")
+    static void assertRepositoryURIIsSecure(final String repositoryName, final String projectPath, final URI uri) {
+        if (uri != null && ["file", "https", "s3"].contains(uri.getScheme()) == false) {
+            final String message = String.format(
+                    Locale.ROOT,
+                    "repository [%s] on project with path [%s] is not using a secure protocol for artifacts on [%s]",
+                    repositoryName,
+                    projectPath,
+                    uri.toURL())
+            throw new GradleException(message)
         }
     }
 

+ 2 - 2
buildSrc/src/main/groovy/org/elasticsearch/gradle/doc/SnippetsTask.groovy

@@ -123,7 +123,7 @@ public class SnippetsTask extends DefaultTask {
                     }
                 }
                 if (snippet.testResponse
-                        && 'js' == snippet.language
+                        && ('js' == snippet.language || 'console-result' == snippet.language)
                         && null == snippet.skip) {
                     String quoted = snippet.contents
                         // quote values starting with $
@@ -162,7 +162,7 @@ public class SnippetsTask extends DefaultTask {
                     }
                     return
                 }
-                matcher = line =~ /\["?source"?,\s*"?(\w+)"?(,.*)?].*/
+                matcher = line =~ /\["?source"?,\s*"?([-\w]+)"?(,.*)?].*/
                 if (matcher.matches()) {
                     lastLanguage = matcher.group(1)
                     lastLanguageLine = lineNumber

+ 1 - 1
buildSrc/src/main/groovy/org/elasticsearch/gradle/test/ClusterFormationTasks.groovy

@@ -370,7 +370,7 @@ class ClusterFormationTasks {
         Map esConfig = [
                 'cluster.name'                 : node.clusterName,
                 'node.name'                    : "node-" + node.nodeNum,
-                'pidfile'                      : node.pidFile,
+                (node.nodeVersion.onOrAfter('7.4.0') ? 'node.pidfile' : 'pidfile') : node.pidFile,
                 'path.repo'                    : "${node.sharedDir}/repo",
                 'path.shared_data'             : "${node.sharedDir}/",
                 // Define a node attribute so we can test that it exists

+ 24 - 0
buildSrc/src/test/groovy/org/elasticsearch/gradle/BuildPluginTests.java

@@ -22,6 +22,9 @@ import org.elasticsearch.gradle.test.GradleUnitTestCase;
 import org.gradle.api.GradleException;
 import org.junit.Test;
 
+import java.net.URI;
+import java.net.URISyntaxException;
+
 
 public class BuildPluginTests extends GradleUnitTestCase {
 
@@ -36,4 +39,25 @@ public class BuildPluginTests extends GradleUnitTestCase {
         BuildPlugin.checkDockerVersionRecent("Docker version 17.04.0, build e68fc7a");
     }
 
+    @Test(expected = GradleException.class)
+    public void testRepositoryURIThatUsesHttpScheme() throws URISyntaxException {
+        final URI uri = new URI("http://s3.amazonaws.com/artifacts.elastic.co/maven");
+        BuildPlugin.assertRepositoryURIIsSecure("test", "test", uri);
+    }
+
+    public void testRepositoryThatUsesFileScheme() throws URISyntaxException {
+        final URI uri = new URI("file:/tmp/maven");
+        BuildPlugin.assertRepositoryURIIsSecure("test", "test", uri);
+    }
+
+    public void testRepositoryURIThatUsesHttpsScheme() throws URISyntaxException {
+        final URI uri = new URI("https://s3.amazonaws.com/artifacts.elastic.co/maven");
+        BuildPlugin.assertRepositoryURIIsSecure("test", "test", uri);
+    }
+
+    public void testRepositoryURIThatUsesS3Scheme() throws URISyntaxException {
+        final URI uri = new URI("s3://artifacts.elastic.co/maven");
+        BuildPlugin.assertRepositoryURIIsSecure("test", "test", uri);
+    }
+
 }

+ 55 - 0
buildSrc/src/test/java/org/elasticsearch/gradle/BuildPluginIT.java

@@ -18,19 +18,29 @@
  */
 package org.elasticsearch.gradle;
 
+import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
 import org.elasticsearch.gradle.test.GradleIntegrationTestCase;
 import org.gradle.testkit.runner.BuildResult;
+import org.gradle.testkit.runner.GradleRunner;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
 
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.List;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
 public class BuildPluginIT extends GradleIntegrationTestCase {
 
+    @Rule
+    public TemporaryFolder tmpDir = new TemporaryFolder();
+
     public void testPluginCanBeApplied() {
         BuildResult result = getGradleRunner("elasticsearch.build")
             .withArguments("hello", "-s")
@@ -46,6 +56,51 @@ public class BuildPluginIT extends GradleIntegrationTestCase {
         assertTaskSuccessful(result, ":check");
     }
 
+    public void testInsecureMavenRepository() throws IOException {
+        final String name = "elastic-maven";
+        final String url = "http://s3.amazonaws.com/artifacts.elastic.co/maven";
+        // add an insecure maven repository to the build.gradle
+        final List<String> lines = Arrays.asList(
+            "repositories {",
+            "  maven {",
+            "    name \"elastic-maven\"",
+            "    url \"" +  url + "\"\n",
+            "  }",
+            "}");
+        runInsecureArtifactRepositoryTest(name, url, lines);
+    }
+
+    public void testInsecureIvyRepository() throws IOException {
+        final String name = "elastic-ivy";
+        final String url = "http://s3.amazonaws.com/artifacts.elastic.co/ivy";
+        // add an insecure ivy repository to the build.gradle
+        final List<String> lines = Arrays.asList(
+            "repositories {",
+            "  ivy {",
+            "    name \"elastic-ivy\"",
+            "    url \"" +  url + "\"\n",
+            "  }",
+            "}");
+        runInsecureArtifactRepositoryTest(name, url, lines);
+    }
+
+    private void runInsecureArtifactRepositoryTest(final String name, final String url, final List<String> lines) throws IOException {
+        final File projectDir = getProjectDir("elasticsearch.build");
+        FileUtils.copyDirectory(projectDir, tmpDir.getRoot(), pathname -> pathname.getPath().contains("/build/") == false);
+        final List<String> buildGradleLines =
+            Files.readAllLines(tmpDir.getRoot().toPath().resolve("build.gradle"), StandardCharsets.UTF_8);
+        buildGradleLines.addAll(lines);
+        Files.write(tmpDir.getRoot().toPath().resolve("build.gradle"), buildGradleLines, StandardCharsets.UTF_8);
+        final BuildResult result = GradleRunner.create()
+            .withProjectDir(tmpDir.getRoot())
+            .withArguments("clean", "hello", "-s", "-i", "--warning-mode=all", "--scan")
+            .withPluginClasspath()
+            .buildAndFail();
+        assertOutputContains(
+            result.getOutput(),
+            "repository [" + name + "] on project with path [:] is not using a secure protocol for artifacts on [" + url + "]");
+    }
+
     public void testLicenseAndNotice() throws IOException {
         BuildResult result = getGradleRunner("elasticsearch.build")
             .withArguments("clean", "assemble", "-s", "-Dlocal.repo.path=" + getLocalTestRepoPath())

+ 9 - 0
client/rest-high-level/build.gradle

@@ -85,6 +85,7 @@ processTestResources {
   from({ zipTree(configurations.restSpec.singleFile) }) {
     include 'rest-api-spec/api/**'
   }
+  from(project(':client:rest-high-level').file('src/test/resources'))
 }
 
 dependencyLicenses {
@@ -117,6 +118,7 @@ if (isEclipse) {
 
 File nodeCert = file("./testnode.crt")
 File nodeTrustStore = file("./testnode.jks")
+File pkiTrustCert = file("./src/test/resources/org/elasticsearch/client/security/delegate_pki/testRootCA.crt")
 
 integTest.runner {
   systemProperty 'tests.rest.cluster.username', System.getProperty('tests.rest.cluster.username', 'test_user')
@@ -134,6 +136,12 @@ testClusters.integTest {
   // Truststore settings are not used since TLS is not enabled. Included for testing the get certificates API
   setting 'xpack.security.http.ssl.certificate_authorities', 'testnode.crt'
   setting 'xpack.security.transport.ssl.truststore.path', 'testnode.jks'
+  setting 'xpack.security.authc.realms.file.default_file.order', '0'
+  setting 'xpack.security.authc.realms.native.default_native.order', '1'
+  setting 'xpack.security.authc.realms.pki.pki1.order', '2'
+  setting 'xpack.security.authc.realms.pki.pki1.certificate_authorities', '[ "testRootCA.crt" ]'
+  setting 'xpack.security.authc.realms.pki.pki1.delegation.enabled', 'true'
+
   setting 'indices.lifecycle.poll_interval', '1000ms'
   keystore 'xpack.security.transport.ssl.truststore.secure_password', 'testnode'
   user username:  System.getProperty('tests.rest.cluster.username', 'test_user'),
@@ -141,4 +149,5 @@ testClusters.integTest {
 
   extraConfigFile nodeCert.name, nodeCert
   extraConfigFile nodeTrustStore.name, nodeTrustStore
+  extraConfigFile pkiTrustCert.name, pkiTrustCert
 }

+ 53 - 53
client/rest-high-level/src/main/java/org/elasticsearch/client/CcrClient.java

@@ -85,15 +85,15 @@ public final class CcrClient {
      *
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ccr-put-follow.html">
      * the docs</a> for more.
-     *
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putFollowAsync(PutFollowRequest request,
-                               RequestOptions options,
-                               ActionListener<PutFollowResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(
+    public Cancellable putFollowAsync(PutFollowRequest request,
+                                      RequestOptions options,
+                                      ActionListener<PutFollowResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
             request,
             CcrRequestConverters::putFollow,
             options,
@@ -129,15 +129,15 @@ public final class CcrClient {
      *
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ccr-pause-follow.html">
      * the docs</a> for more.
-     *
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void pauseFollowAsync(PauseFollowRequest request,
-                                 RequestOptions options,
-                                 ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(
+    public Cancellable pauseFollowAsync(PauseFollowRequest request,
+                                        RequestOptions options,
+                                        ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
             request,
             CcrRequestConverters::pauseFollow,
             options,
@@ -172,15 +172,15 @@ public final class CcrClient {
      *
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ccr-resume-follow.html">
      * the docs</a> for more.
-     *
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void resumeFollowAsync(ResumeFollowRequest request,
-                                  RequestOptions options,
-                                  ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(
+    public Cancellable resumeFollowAsync(ResumeFollowRequest request,
+                                         RequestOptions options,
+                                         ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
             request,
             CcrRequestConverters::resumeFollow,
             options,
@@ -217,15 +217,15 @@ public final class CcrClient {
      *
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ccr-unfollow.html">
      * the docs</a> for more.
-     *
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void unfollowAsync(UnfollowRequest request,
-                              RequestOptions options,
-                              ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(
+    public Cancellable unfollowAsync(UnfollowRequest request,
+                                     RequestOptions options,
+                                     ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
             request,
             CcrRequestConverters::unfollow,
             options,
@@ -260,15 +260,15 @@ public final class CcrClient {
      *
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ccr-forget-follower.html">the docs</a> for more details
      * on the intended usage of this API.
-     *
      * @param request the request
      * @param options the request options (e.g., headers), use {@link RequestOptions#DEFAULT} if the defaults are acceptable.
+     * @return cancellable that may be used to cancel the request
      */
-    public void forgetFollowerAsync(
+    public Cancellable forgetFollowerAsync(
             final ForgetFollowerRequest request,
             final RequestOptions options,
             final ActionListener<BroadcastResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
                 request,
                 CcrRequestConverters::forgetFollower,
                 options,
@@ -303,15 +303,15 @@ public final class CcrClient {
      *
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ccr-put-auto-follow-pattern.html">
      * the docs</a> for more.
-     *
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putAutoFollowPatternAsync(PutAutoFollowPatternRequest request,
-                                          RequestOptions options,
-                                          ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(
+    public Cancellable putAutoFollowPatternAsync(PutAutoFollowPatternRequest request,
+                                                 RequestOptions options,
+                                                 ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
             request,
             CcrRequestConverters::putAutoFollowPattern,
             options,
@@ -347,15 +347,15 @@ public final class CcrClient {
      *
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ccr-delete-auto-follow-pattern.html">
      * the docs</a> for more.
-     *
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteAutoFollowPatternAsync(DeleteAutoFollowPatternRequest request,
-                                             RequestOptions options,
-                                             ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(
+    public Cancellable deleteAutoFollowPatternAsync(DeleteAutoFollowPatternRequest request,
+                                                    RequestOptions options,
+                                                    ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
             request,
             CcrRequestConverters::deleteAutoFollowPattern,
             options,
@@ -392,15 +392,15 @@ public final class CcrClient {
      *
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ccr-get-auto-follow-pattern.html">
      * the docs</a> for more.
-     *
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getAutoFollowPatternAsync(GetAutoFollowPatternRequest request,
-                                          RequestOptions options,
-                                          ActionListener<GetAutoFollowPatternResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(
+    public Cancellable getAutoFollowPatternAsync(GetAutoFollowPatternRequest request,
+                                                 RequestOptions options,
+                                                 ActionListener<GetAutoFollowPatternResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
             request,
             CcrRequestConverters::getAutoFollowPattern,
             options,
@@ -437,14 +437,14 @@ public final class CcrClient {
      *
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ccr-get-stats.html">
      * the docs</a> for more.
-     *
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
+     * @return cancellable that may be used to cancel the request
      */
-    public void getCcrStatsAsync(CcrStatsRequest request,
-                                 RequestOptions options,
-                                 ActionListener<CcrStatsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(
+    public Cancellable getCcrStatsAsync(CcrStatsRequest request,
+                                        RequestOptions options,
+                                        ActionListener<CcrStatsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
             request,
             CcrRequestConverters::getCcrStats,
             options,
@@ -481,14 +481,14 @@ public final class CcrClient {
      *
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ccr-get-follow-stats.html">
      * the docs</a> for more.
-     *
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
+     * @return cancellable that may be used to cancel the request
      */
-    public void getFollowStatsAsync(FollowStatsRequest request,
-                                    RequestOptions options,
-                                    ActionListener<FollowStatsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(
+    public Cancellable getFollowStatsAsync(FollowStatsRequest request,
+                                           RequestOptions options,
+                                           ActionListener<FollowStatsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
             request,
             CcrRequestConverters::getFollowStats,
             options,
@@ -524,14 +524,14 @@ public final class CcrClient {
      *
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ccr-get-follow-info.html">
      * the docs</a> for more.
-     *
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
+     * @return cancellable that may be used to cancel the request
      */
-    public void getFollowInfoAsync(FollowInfoRequest request,
-                                   RequestOptions options,
-                                   ActionListener<FollowInfoResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(
+    public Cancellable getFollowInfoAsync(FollowInfoRequest request,
+                                          RequestOptions options,
+                                          ActionListener<FollowInfoResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
             request,
             CcrRequestConverters::getFollowInfo,
             options,

+ 14 - 8
client/rest-high-level/src/main/java/org/elasticsearch/client/ClusterClient.java

@@ -67,10 +67,12 @@ public final class ClusterClient {
      * @param clusterUpdateSettingsRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putSettingsAsync(ClusterUpdateSettingsRequest clusterUpdateSettingsRequest, RequestOptions options,
-                                 ActionListener<ClusterUpdateSettingsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(clusterUpdateSettingsRequest, ClusterRequestConverters::clusterPutSettings,
+    public Cancellable putSettingsAsync(ClusterUpdateSettingsRequest clusterUpdateSettingsRequest, RequestOptions options,
+                                        ActionListener<ClusterUpdateSettingsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(clusterUpdateSettingsRequest,
+                ClusterRequestConverters::clusterPutSettings,
                 options, ClusterUpdateSettingsResponse::fromXContent, listener, emptySet());
     }
 
@@ -96,10 +98,12 @@ public final class ClusterClient {
      * @param clusterGetSettingsRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getSettingsAsync(ClusterGetSettingsRequest clusterGetSettingsRequest, RequestOptions options,
-                                 ActionListener<ClusterGetSettingsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(clusterGetSettingsRequest, ClusterRequestConverters::clusterGetSettings,
+    public Cancellable getSettingsAsync(ClusterGetSettingsRequest clusterGetSettingsRequest, RequestOptions options,
+                                        ActionListener<ClusterGetSettingsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
+            clusterGetSettingsRequest, ClusterRequestConverters::clusterGetSettings,
             options, ClusterGetSettingsResponse::fromXContent, listener, emptySet());
     }
 
@@ -127,9 +131,11 @@ public final class ClusterClient {
      * @param healthRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void healthAsync(ClusterHealthRequest healthRequest, RequestOptions options, ActionListener<ClusterHealthResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(healthRequest, ClusterRequestConverters::clusterHealth, options,
+    public Cancellable healthAsync(ClusterHealthRequest healthRequest, RequestOptions options,
+                                   ActionListener<ClusterHealthResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(healthRequest, ClusterRequestConverters::clusterHealth, options,
                 ClusterHealthResponse::fromXContent, listener, singleton(RestStatus.REQUEST_TIMEOUT.getStatus()));
     }
 }

+ 33 - 33
client/rest-high-level/src/main/java/org/elasticsearch/client/DataFrameClient.java

@@ -74,15 +74,15 @@ public final class DataFrameClient {
      * For additional info
      * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/put-data-frame-transform.html">
      *     Create data frame transform documentation</a>
-     *
      * @param request The PutDataFrameTransformRequest containing the
      * {@link org.elasticsearch.client.dataframe.transforms.DataFrameTransformConfig}.
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putDataFrameTransformAsync(PutDataFrameTransformRequest request, RequestOptions options,
-                                      ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable putDataFrameTransformAsync(PutDataFrameTransformRequest request, RequestOptions options,
+                                                  ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 DataFrameRequestConverters::putDataFrameTransform,
                 options,
                 AcknowledgedResponse::fromXContent,
@@ -118,16 +118,16 @@ public final class DataFrameClient {
      * For additional info
      * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/update-data-frame-transform.html">
      *     Create data frame transform documentation</a>
-     *
      * @param request The UpdateDataFrameTransformRequest containing the
      * {@link org.elasticsearch.client.dataframe.transforms.DataFrameTransformConfigUpdate}.
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void updateDataFrameTransformAsync(UpdateDataFrameTransformRequest request,
-                                              RequestOptions options,
-                                              ActionListener<UpdateDataFrameTransformResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable updateDataFrameTransformAsync(UpdateDataFrameTransformRequest request,
+                                                     RequestOptions options,
+                                                     ActionListener<UpdateDataFrameTransformResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             DataFrameRequestConverters::updateDataFrameTransform,
             options,
             UpdateDataFrameTransformResponse::fromXContent,
@@ -162,14 +162,14 @@ public final class DataFrameClient {
      * For additional info
      * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/get-data-frame-transform-stats.html">
      *     Get data frame transform stats documentation</a>
-     *
      * @param request Specifies the which transforms to get the stats for
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getDataFrameTransformStatsAsync(GetDataFrameTransformStatsRequest request, RequestOptions options,
-                                           ActionListener<GetDataFrameTransformStatsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getDataFrameTransformStatsAsync(GetDataFrameTransformStatsRequest request, RequestOptions options,
+                                                       ActionListener<GetDataFrameTransformStatsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 DataFrameRequestConverters::getDataFrameTransformStats,
                 options,
                 GetDataFrameTransformStatsResponse::fromXContent,
@@ -204,14 +204,14 @@ public final class DataFrameClient {
      * For additional info
      * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/delete-data-frame-transform.html">
      *     Delete data frame transform documentation</a>
-     *
      * @param request The delete data frame transform request
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteDataFrameTransformAsync(DeleteDataFrameTransformRequest request, RequestOptions options,
-                                              ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable deleteDataFrameTransformAsync(DeleteDataFrameTransformRequest request, RequestOptions options,
+                                                     ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 DataFrameRequestConverters::deleteDataFrameTransform,
                 options,
                 AcknowledgedResponse::fromXContent,
@@ -245,14 +245,14 @@ public final class DataFrameClient {
      * <p>
      * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/preview-data-frame-transform.html">
      *     Preview data frame transform documentation</a>
-     *
      * @param request The preview data frame transform request
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void previewDataFrameTransformAsync(PreviewDataFrameTransformRequest request, RequestOptions options,
-                                             ActionListener<PreviewDataFrameTransformResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable previewDataFrameTransformAsync(PreviewDataFrameTransformRequest request, RequestOptions options,
+                                                      ActionListener<PreviewDataFrameTransformResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 DataFrameRequestConverters::previewDataFrameTransform,
                 options,
                 PreviewDataFrameTransformResponse::fromXContent,
@@ -287,14 +287,14 @@ public final class DataFrameClient {
      * For additional info
      * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/start-data-frame-transform.html">
      *     Start data frame transform documentation</a>
-     *
      * @param request The start data frame transform request
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void startDataFrameTransformAsync(StartDataFrameTransformRequest request, RequestOptions options,
-                                            ActionListener<StartDataFrameTransformResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable startDataFrameTransformAsync(StartDataFrameTransformRequest request, RequestOptions options,
+                                                    ActionListener<StartDataFrameTransformResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 DataFrameRequestConverters::startDataFrameTransform,
                 options,
                 StartDataFrameTransformResponse::fromXContent,
@@ -329,14 +329,14 @@ public final class DataFrameClient {
      * For additional info
      * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/stop-data-frame-transform.html">
      *     Stop data frame transform documentation</a>
-     *
      * @param request The stop data frame transform request
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void stopDataFrameTransformAsync(StopDataFrameTransformRequest request, RequestOptions options,
-                                            ActionListener<StopDataFrameTransformResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable stopDataFrameTransformAsync(StopDataFrameTransformRequest request, RequestOptions options,
+                                                   ActionListener<StopDataFrameTransformResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 DataFrameRequestConverters::stopDataFrameTransform,
                 options,
                 StopDataFrameTransformResponse::fromXContent,
@@ -371,14 +371,14 @@ public final class DataFrameClient {
      * For additional info
      * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/get-data-frame-transform.html">
      *     Get data frame transform documentation</a>
-     *
      * @param request The get data frame transform request
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getDataFrameTransformAsync(GetDataFrameTransformRequest request, RequestOptions options,
-                                           ActionListener<GetDataFrameTransformResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getDataFrameTransformAsync(GetDataFrameTransformRequest request, RequestOptions options,
+                                                  ActionListener<GetDataFrameTransformResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 DataFrameRequestConverters::getDataFrameTransform,
                 options,
                 GetDataFrameTransformResponse::fromXContent,

+ 8 - 7
client/rest-high-level/src/main/java/org/elasticsearch/client/GraphClient.java

@@ -34,7 +34,7 @@ public class GraphClient {
     GraphClient(RestHighLevelClient restHighLevelClient) {
         this.restHighLevelClient = restHighLevelClient;
     }
-    
+
     /**
      * Executes an exploration request using the Graph API.
      *
@@ -52,12 +52,13 @@ public class GraphClient {
      *
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/graph-explore-api.html">Graph API
      * on elastic.co</a>.
+     * @return cancellable that may be used to cancel the request
      */
-    public final void exploreAsync(GraphExploreRequest graphExploreRequest,
-                                           RequestOptions options,
-                                           ActionListener<GraphExploreResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(graphExploreRequest, GraphRequestConverters::explore,
+    public final Cancellable exploreAsync(GraphExploreRequest graphExploreRequest,
+                                          RequestOptions options,
+                                          ActionListener<GraphExploreResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(graphExploreRequest, GraphRequestConverters::explore,
             options, GraphExploreResponse::fromXContent, listener, emptySet());
-    }    
-    
+    }
+
 }

+ 56 - 38
client/rest-high-level/src/main/java/org/elasticsearch/client/IndexLifecycleClient.java

@@ -74,10 +74,11 @@ public class IndexLifecycleClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getLifecyclePolicyAsync(GetLifecyclePolicyRequest request, RequestOptions options,
-                                        ActionListener<GetLifecyclePolicyResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::getLifecyclePolicy, options,
+    public Cancellable getLifecyclePolicyAsync(GetLifecyclePolicyRequest request, RequestOptions options,
+                                               ActionListener<GetLifecyclePolicyResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::getLifecyclePolicy, options,
             GetLifecyclePolicyResponse::fromXContent, listener, emptySet());
     }
 
@@ -103,10 +104,11 @@ public class IndexLifecycleClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putLifecyclePolicyAsync(PutLifecyclePolicyRequest request, RequestOptions options,
-                                        ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::putLifecyclePolicy, options,
+    public Cancellable putLifecyclePolicyAsync(PutLifecyclePolicyRequest request, RequestOptions options,
+                                               ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::putLifecyclePolicy, options,
             AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -132,10 +134,12 @@ public class IndexLifecycleClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteLifecyclePolicyAsync(DeleteLifecyclePolicyRequest request, RequestOptions options,
-                                           ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::deleteLifecyclePolicy, options,
+    public Cancellable deleteLifecyclePolicyAsync(DeleteLifecyclePolicyRequest request, RequestOptions options,
+                                                  ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
+            request, IndexLifecycleRequestConverters::deleteLifecyclePolicy, options,
             AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -161,10 +165,12 @@ public class IndexLifecycleClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void removeIndexLifecyclePolicyAsync(RemoveIndexLifecyclePolicyRequest request, RequestOptions options,
-            ActionListener<RemoveIndexLifecyclePolicyResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::removeIndexLifecyclePolicy, options,
+    public Cancellable removeIndexLifecyclePolicyAsync(RemoveIndexLifecyclePolicyRequest request, RequestOptions options,
+                                                       ActionListener<RemoveIndexLifecyclePolicyResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
+            request, IndexLifecycleRequestConverters::removeIndexLifecyclePolicy, options,
                 RemoveIndexLifecyclePolicyResponse::fromXContent, listener, emptySet());
     }
 
@@ -189,9 +195,10 @@ public class IndexLifecycleClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void startILMAsync(StartILMRequest request, RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::startILM, options,
+    public Cancellable startILMAsync(StartILMRequest request, RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::startILM, options,
                 AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -227,14 +234,15 @@ public class IndexLifecycleClient {
      * Asynchronously get the status of index lifecycle management
      * See <a href="https://fix-me-when-we-have-docs.com">
      * the docs</a> for more.
-     *
      * @param request  the request
      * @param options  the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void lifecycleManagementStatusAsync(LifecycleManagementStatusRequest request, RequestOptions options,
-                                               ActionListener<LifecycleManagementStatusResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::lifecycleManagementStatus, options,
+    public Cancellable lifecycleManagementStatusAsync(LifecycleManagementStatusRequest request, RequestOptions options,
+                                                      ActionListener<LifecycleManagementStatusResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
+            request, IndexLifecycleRequestConverters::lifecycleManagementStatus, options,
             LifecycleManagementStatusResponse::fromXContent, listener, emptySet());
     }
 
@@ -245,9 +253,10 @@ public class IndexLifecycleClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void stopILMAsync(StopILMRequest request, RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::stopILM, options,
+    public Cancellable stopILMAsync(StopILMRequest request, RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::stopILM, options,
                 AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -272,10 +281,11 @@ public class IndexLifecycleClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void explainLifecycleAsync(ExplainLifecycleRequest request, RequestOptions options,
-                                      ActionListener<ExplainLifecycleResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::explainLifecycle, options,
+    public Cancellable explainLifecycleAsync(ExplainLifecycleRequest request, RequestOptions options,
+                                             ActionListener<ExplainLifecycleResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::explainLifecycle, options,
                 ExplainLifecycleResponse::fromXContent, listener, emptySet());
     }
 
@@ -300,10 +310,11 @@ public class IndexLifecycleClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void retryLifecyclePolicyAsync(RetryLifecyclePolicyRequest request, RequestOptions options,
-                                          ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::retryLifecycle, options,
+    public Cancellable retryLifecyclePolicyAsync(RetryLifecyclePolicyRequest request, RequestOptions options,
+                                                 ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::retryLifecycle, options,
             AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -335,10 +346,11 @@ public class IndexLifecycleClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getSnapshotLifecyclePolicyAsync(GetSnapshotLifecyclePolicyRequest request, RequestOptions options,
-                                                ActionListener<GetSnapshotLifecyclePolicyResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::getSnapshotLifecyclePolicy,
+    public Cancellable getSnapshotLifecyclePolicyAsync(GetSnapshotLifecyclePolicyRequest request, RequestOptions options,
+                                                       ActionListener<GetSnapshotLifecyclePolicyResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::getSnapshotLifecyclePolicy,
             options, GetSnapshotLifecyclePolicyResponse::fromXContent, listener, emptySet());
     }
 
@@ -370,10 +382,11 @@ public class IndexLifecycleClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putSnapshotLifecyclePolicyAsync(PutSnapshotLifecyclePolicyRequest request, RequestOptions options,
-                                                ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::putSnapshotLifecyclePolicy,
+    public Cancellable putSnapshotLifecyclePolicyAsync(PutSnapshotLifecyclePolicyRequest request, RequestOptions options,
+                                                       ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::putSnapshotLifecyclePolicy,
             options, AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -405,10 +418,12 @@ public class IndexLifecycleClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteSnapshotLifecyclePolicyAsync(DeleteSnapshotLifecyclePolicyRequest request, RequestOptions options,
-                                                   ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::deleteSnapshotLifecyclePolicy,
+    public Cancellable deleteSnapshotLifecyclePolicyAsync(DeleteSnapshotLifecyclePolicyRequest request,
+                                          RequestOptions options,ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
+            request, IndexLifecycleRequestConverters::deleteSnapshotLifecyclePolicy,
             options, AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -440,10 +455,13 @@ public class IndexLifecycleClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void executeSnapshotLifecyclePolicyAsync(ExecuteSnapshotLifecyclePolicyRequest request, RequestOptions options,
-                                                    ActionListener<ExecuteSnapshotLifecyclePolicyResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, IndexLifecycleRequestConverters::executeSnapshotLifecyclePolicy,
+    public Cancellable executeSnapshotLifecyclePolicyAsync(
+        ExecuteSnapshotLifecyclePolicyRequest request, RequestOptions options,
+        ActionListener<ExecuteSnapshotLifecyclePolicyResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
+            request, IndexLifecycleRequestConverters::executeSnapshotLifecyclePolicy,
             options, ExecuteSnapshotLifecyclePolicyResponse::fromXContent, listener, emptySet());
     }
 }

+ 200 - 133
client/rest-high-level/src/main/java/org/elasticsearch/client/IndicesClient.java

@@ -108,9 +108,12 @@ public final class IndicesClient {
      * @param deleteIndexRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteAsync(DeleteIndexRequest deleteIndexRequest, RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(deleteIndexRequest, IndicesRequestConverters::deleteIndex, options,
+    public Cancellable deleteAsync(DeleteIndexRequest deleteIndexRequest, RequestOptions options,
+                                   ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(deleteIndexRequest,
+            IndicesRequestConverters::deleteIndex, options,
             AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -136,11 +139,12 @@ public final class IndicesClient {
      * @param createIndexRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void createAsync(CreateIndexRequest createIndexRequest,
-                            RequestOptions options,
-                            ActionListener<CreateIndexResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(createIndexRequest, IndicesRequestConverters::createIndex, options,
+    public Cancellable createAsync(CreateIndexRequest createIndexRequest,
+                                   RequestOptions options,
+                                   ActionListener<CreateIndexResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(createIndexRequest, IndicesRequestConverters::createIndex, options,
             CreateIndexResponse::fromXContent, listener, emptySet());
     }
 
@@ -177,12 +181,13 @@ public final class IndicesClient {
      * @deprecated This method uses an old request object which still refers to types, a deprecated feature. The
      * method {@link #createAsync(CreateIndexRequest, RequestOptions, ActionListener)} should be used instead,
      * which accepts a new request object.
+     * @return cancellable that may be used to cancel the request
      */
     @Deprecated
-    public void createAsync(org.elasticsearch.action.admin.indices.create.CreateIndexRequest createIndexRequest,
-                            RequestOptions options,
-                            ActionListener<org.elasticsearch.action.admin.indices.create.CreateIndexResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(createIndexRequest,
+    public Cancellable createAsync(org.elasticsearch.action.admin.indices.create.CreateIndexRequest createIndexRequest,
+                                   RequestOptions options,
+                                   ActionListener<org.elasticsearch.action.admin.indices.create.CreateIndexResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(createIndexRequest,
             IndicesRequestConverters::createIndex, options,
             org.elasticsearch.action.admin.indices.create.CreateIndexResponse::fromXContent, listener, emptySet());
     }
@@ -208,10 +213,11 @@ public final class IndicesClient {
      * @param putMappingRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putMappingAsync(PutMappingRequest putMappingRequest, RequestOptions options,
-                                ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(putMappingRequest, IndicesRequestConverters::putMapping, options,
+    public Cancellable putMappingAsync(PutMappingRequest putMappingRequest, RequestOptions options,
+                                       ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(putMappingRequest, IndicesRequestConverters::putMapping, options,
             AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -245,12 +251,13 @@ public final class IndicesClient {
      * @deprecated This method uses an old request object which still refers to types, a deprecated feature. The
      * method {@link #putMappingAsync(PutMappingRequest, RequestOptions, ActionListener)} should be used instead,
      * which accepts a new request object.
+     * @return cancellable that may be used to cancel the request
      */
     @Deprecated
-    public void putMappingAsync(org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest putMappingRequest,
-                                RequestOptions options,
-                                ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(putMappingRequest, IndicesRequestConverters::putMapping, options,
+    public Cancellable putMappingAsync(org.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest putMappingRequest,
+                                       RequestOptions options,
+                                       ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(putMappingRequest, IndicesRequestConverters::putMapping, options,
             AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -278,10 +285,11 @@ public final class IndicesClient {
      * @param getMappingsRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getMappingAsync(GetMappingsRequest getMappingsRequest, RequestOptions options,
-                                ActionListener<GetMappingsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(getMappingsRequest,
+    public Cancellable getMappingAsync(GetMappingsRequest getMappingsRequest, RequestOptions options,
+                                       ActionListener<GetMappingsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(getMappingsRequest,
             IndicesRequestConverters::getMappings,
             options,
             GetMappingsResponse::fromXContent,
@@ -324,12 +332,13 @@ public final class IndicesClient {
      * @deprecated This method uses old request and response objects which still refer to types, a deprecated feature.
      * The method {@link #getMapping(GetMappingsRequest, RequestOptions)} should be used instead, which accepts a new
      * request object.
+     * @return cancellable that may be used to cancel the request
      */
     @Deprecated
-    public void getMappingAsync(org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest getMappingsRequest,
-                                RequestOptions options,
-                                ActionListener<org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(getMappingsRequest,
+    public Cancellable getMappingAsync(org.elasticsearch.action.admin.indices.mapping.get.GetMappingsRequest getMappingsRequest,
+                                       RequestOptions options,
+                                       ActionListener<org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(getMappingsRequest,
             IndicesRequestConverters::getMappings,
             options,
             org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse::fromXContent,
@@ -369,13 +378,17 @@ public final class IndicesClient {
      * @deprecated This method uses old request and response objects which still refer to types, a deprecated feature.
      * The method {@link #getFieldMappingAsync(GetFieldMappingsRequest, RequestOptions, ActionListener)} should be
      * used instead, which accepts a new request object.
+     * @return cancellable that may be used to cancel the request
      */
     @Deprecated
-    public void getFieldMappingAsync(org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsRequest getFieldMappingsRequest,
-            RequestOptions options,
-            ActionListener<org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(getFieldMappingsRequest, IndicesRequestConverters::getFieldMapping, options,
-            org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse::fromXContent, listener, emptySet());
+    public Cancellable getFieldMappingAsync(
+        org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsRequest getFieldMappingsRequest,
+        RequestOptions options,
+        ActionListener<org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(getFieldMappingsRequest,
+            IndicesRequestConverters::getFieldMapping, options,
+            org.elasticsearch.action.admin.indices.mapping.get.GetFieldMappingsResponse::fromXContent,
+            listener, emptySet());
     }
 
     /**
@@ -401,10 +414,12 @@ public final class IndicesClient {
      * @param getFieldMappingsRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getFieldMappingAsync(GetFieldMappingsRequest getFieldMappingsRequest,
-            RequestOptions options, ActionListener<GetFieldMappingsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(getFieldMappingsRequest, IndicesRequestConverters::getFieldMapping, options,
+    public Cancellable getFieldMappingAsync(GetFieldMappingsRequest getFieldMappingsRequest,
+                                            RequestOptions options, ActionListener<GetFieldMappingsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
+            getFieldMappingsRequest, IndicesRequestConverters::getFieldMapping, options,
             GetFieldMappingsResponse::fromXContent, listener, emptySet());
     }
 
@@ -429,10 +444,12 @@ public final class IndicesClient {
      * @param indicesAliasesRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void updateAliasesAsync(IndicesAliasesRequest indicesAliasesRequest, RequestOptions options,
-                                   ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(indicesAliasesRequest, IndicesRequestConverters::updateAliases, options,
+    public Cancellable updateAliasesAsync(IndicesAliasesRequest indicesAliasesRequest, RequestOptions options,
+                                          ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(indicesAliasesRequest,
+            IndicesRequestConverters::updateAliases, options,
             AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -457,9 +474,10 @@ public final class IndicesClient {
      * @param openIndexRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void openAsync(OpenIndexRequest openIndexRequest, RequestOptions options, ActionListener<OpenIndexResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(openIndexRequest, IndicesRequestConverters::openIndex, options,
+    public Cancellable openAsync(OpenIndexRequest openIndexRequest, RequestOptions options, ActionListener<OpenIndexResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(openIndexRequest, IndicesRequestConverters::openIndex, options,
                 OpenIndexResponse::fromXContent, listener, emptySet());
     }
 
@@ -484,9 +502,12 @@ public final class IndicesClient {
      * @param closeIndexRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void closeAsync(CloseIndexRequest closeIndexRequest, RequestOptions options, ActionListener<CloseIndexResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(closeIndexRequest, IndicesRequestConverters::closeIndex, options,
+    public Cancellable closeAsync(CloseIndexRequest closeIndexRequest, RequestOptions options,
+                                  ActionListener<CloseIndexResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(closeIndexRequest,
+            IndicesRequestConverters::closeIndex, options,
             CloseIndexResponse::fromXContent, listener, emptySet());
     }
 
@@ -512,9 +533,10 @@ public final class IndicesClient {
      * @param getAliasesRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void existsAliasAsync(GetAliasesRequest getAliasesRequest, RequestOptions options, ActionListener<Boolean> listener) {
-        restHighLevelClient.performRequestAsync(getAliasesRequest, IndicesRequestConverters::existsAlias, options,
+    public Cancellable existsAliasAsync(GetAliasesRequest getAliasesRequest, RequestOptions options, ActionListener<Boolean> listener) {
+        return restHighLevelClient.performRequestAsync(getAliasesRequest, IndicesRequestConverters::existsAlias, options,
                 RestHighLevelClient::convertExistsResponse, listener, emptySet());
     }
 
@@ -537,9 +559,10 @@ public final class IndicesClient {
      * @param refreshRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void refreshAsync(RefreshRequest refreshRequest, RequestOptions options, ActionListener<RefreshResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(refreshRequest, IndicesRequestConverters::refresh, options,
+    public Cancellable refreshAsync(RefreshRequest refreshRequest, RequestOptions options, ActionListener<RefreshResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(refreshRequest, IndicesRequestConverters::refresh, options,
                 RefreshResponse::fromXContent, listener, emptySet());
     }
 
@@ -562,9 +585,10 @@ public final class IndicesClient {
      * @param flushRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void flushAsync(FlushRequest flushRequest, RequestOptions options, ActionListener<FlushResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(flushRequest, IndicesRequestConverters::flush, options,
+    public Cancellable flushAsync(FlushRequest flushRequest, RequestOptions options, ActionListener<FlushResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(flushRequest, IndicesRequestConverters::flush, options,
                 FlushResponse::fromXContent, listener, emptySet());
     }
 
@@ -589,10 +613,11 @@ public final class IndicesClient {
      * @param syncedFlushRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void flushSyncedAsync(SyncedFlushRequest syncedFlushRequest, RequestOptions options,
-                                 ActionListener<SyncedFlushResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(syncedFlushRequest, IndicesRequestConverters::flushSynced, options,
+    public Cancellable flushSyncedAsync(SyncedFlushRequest syncedFlushRequest, RequestOptions options,
+                                        ActionListener<SyncedFlushResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(syncedFlushRequest, IndicesRequestConverters::flushSynced, options,
                 SyncedFlushResponse::fromXContent, listener, emptySet());
     }
 
@@ -617,10 +642,11 @@ public final class IndicesClient {
      * @param getSettingsRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getSettingsAsync(GetSettingsRequest getSettingsRequest, RequestOptions options,
-                                 ActionListener<GetSettingsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(getSettingsRequest, IndicesRequestConverters::getSettings, options,
+    public Cancellable getSettingsAsync(GetSettingsRequest getSettingsRequest, RequestOptions options,
+                                        ActionListener<GetSettingsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(getSettingsRequest, IndicesRequestConverters::getSettings, options,
             GetSettingsResponse::fromXContent, listener, emptySet());
     }
 
@@ -645,10 +671,11 @@ public final class IndicesClient {
      * @param getIndexRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getAsync(GetIndexRequest getIndexRequest, RequestOptions options,
-                         ActionListener<GetIndexResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(getIndexRequest, IndicesRequestConverters::getIndex, options,
+    public Cancellable getAsync(GetIndexRequest getIndexRequest, RequestOptions options,
+                                ActionListener<GetIndexResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(getIndexRequest, IndicesRequestConverters::getIndex, options,
             GetIndexResponse::fromXContent, listener, emptySet());
     }
 
@@ -679,11 +706,12 @@ public final class IndicesClient {
      * @param listener the listener to be notified upon request completion
      * @deprecated This method uses an old request object which still refers to types, a deprecated feature. The method
      * {@link #getAsync(GetIndexRequest, RequestOptions, ActionListener)} should be used instead, which accepts a new request object.
+     * @return cancellable that may be used to cancel the request
      */
     @Deprecated
-    public void getAsync(org.elasticsearch.action.admin.indices.get.GetIndexRequest getIndexRequest, RequestOptions options,
-            ActionListener<org.elasticsearch.action.admin.indices.get.GetIndexResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(getIndexRequest, IndicesRequestConverters::getIndex, options,
+    public Cancellable getAsync(org.elasticsearch.action.admin.indices.get.GetIndexRequest getIndexRequest, RequestOptions options,
+                                ActionListener<org.elasticsearch.action.admin.indices.get.GetIndexResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(getIndexRequest, IndicesRequestConverters::getIndex, options,
                 org.elasticsearch.action.admin.indices.get.GetIndexResponse::fromXContent, listener, emptySet());
     }
 
@@ -724,10 +752,12 @@ public final class IndicesClient {
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
      * @deprecated use {@link #forcemergeAsync(ForceMergeRequest, RequestOptions, ActionListener)} instead
+     * @return cancellable that may be used to cancel the request
      */
     @Deprecated
-    public void forceMergeAsync(ForceMergeRequest forceMergeRequest, RequestOptions options, ActionListener<ForceMergeResponse> listener) {
-        forcemergeAsync(forceMergeRequest, options, listener);
+    public Cancellable forceMergeAsync(ForceMergeRequest forceMergeRequest, RequestOptions options,
+                                       ActionListener<ForceMergeResponse> listener) {
+        return forcemergeAsync(forceMergeRequest, options, listener);
     }
 
     /**
@@ -737,9 +767,12 @@ public final class IndicesClient {
      * @param forceMergeRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void forcemergeAsync(ForceMergeRequest forceMergeRequest, RequestOptions options, ActionListener<ForceMergeResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(forceMergeRequest, IndicesRequestConverters::forceMerge, options,
+    public Cancellable forcemergeAsync(ForceMergeRequest forceMergeRequest, RequestOptions options,
+                                       ActionListener<ForceMergeResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(forceMergeRequest,
+                IndicesRequestConverters::forceMerge, options,
                 ForceMergeResponse::fromXContent, listener, emptySet());
     }
 
@@ -765,10 +798,12 @@ public final class IndicesClient {
      * @param clearIndicesCacheRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void clearCacheAsync(ClearIndicesCacheRequest clearIndicesCacheRequest, RequestOptions options,
-                                ActionListener<ClearIndicesCacheResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(clearIndicesCacheRequest, IndicesRequestConverters::clearCache, options,
+    public Cancellable clearCacheAsync(ClearIndicesCacheRequest clearIndicesCacheRequest, RequestOptions options,
+                                       ActionListener<ClearIndicesCacheResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(clearIndicesCacheRequest,
+                IndicesRequestConverters::clearCache, options,
                 ClearIndicesCacheResponse::fromXContent, listener, emptySet());
     }
 
@@ -798,9 +833,10 @@ public final class IndicesClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void existsAsync(GetIndexRequest request, RequestOptions options, ActionListener<Boolean> listener) {
-        restHighLevelClient.performRequestAsync(
+    public Cancellable existsAsync(GetIndexRequest request, RequestOptions options, ActionListener<Boolean> listener) {
+        return restHighLevelClient.performRequestAsync(
                 request,
                 IndicesRequestConverters::indicesExist,
                 options,
@@ -841,11 +877,12 @@ public final class IndicesClient {
      * @param listener the listener to be notified upon request completion
      * @deprecated This method uses an old request object which still refers to types, a deprecated feature. The method
      * {@link #existsAsync(GetIndexRequest, RequestOptions, ActionListener)} should be used instead, which accepts a new request object.
+     * @return cancellable that may be used to cancel the request
      */
     @Deprecated
-    public void existsAsync(org.elasticsearch.action.admin.indices.get.GetIndexRequest request, RequestOptions options,
-            ActionListener<Boolean> listener) {
-        restHighLevelClient.performRequestAsync(
+    public Cancellable existsAsync(org.elasticsearch.action.admin.indices.get.GetIndexRequest request, RequestOptions options,
+                                   ActionListener<Boolean> listener) {
+        return restHighLevelClient.performRequestAsync(
                 request,
                 IndicesRequestConverters::indicesExist,
                 options,
@@ -876,9 +913,10 @@ public final class IndicesClient {
      * @param resizeRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void shrinkAsync(ResizeRequest resizeRequest, RequestOptions options, ActionListener<ResizeResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(resizeRequest, IndicesRequestConverters::shrink, options,
+    public Cancellable shrinkAsync(ResizeRequest resizeRequest, RequestOptions options, ActionListener<ResizeResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(resizeRequest, IndicesRequestConverters::shrink, options,
                 ResizeResponse::fromXContent, listener, emptySet());
     }
 
@@ -903,9 +941,10 @@ public final class IndicesClient {
      * @param resizeRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void splitAsync(ResizeRequest resizeRequest, RequestOptions options, ActionListener<ResizeResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(resizeRequest, IndicesRequestConverters::split, options,
+    public Cancellable splitAsync(ResizeRequest resizeRequest, RequestOptions options, ActionListener<ResizeResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(resizeRequest, IndicesRequestConverters::split, options,
                 ResizeResponse::fromXContent, listener, emptySet());
     }
 
@@ -930,9 +969,10 @@ public final class IndicesClient {
      * @param resizeRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void cloneAsync(ResizeRequest resizeRequest, RequestOptions options, ActionListener<ResizeResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(resizeRequest, IndicesRequestConverters::clone, options,
+    public Cancellable cloneAsync(ResizeRequest resizeRequest, RequestOptions options, ActionListener<ResizeResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(resizeRequest, IndicesRequestConverters::clone, options,
             ResizeResponse::fromXContent, listener, emptySet());
     }
 
@@ -957,9 +997,10 @@ public final class IndicesClient {
      * @param rolloverRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void rolloverAsync(RolloverRequest rolloverRequest, RequestOptions options, ActionListener<RolloverResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(rolloverRequest, IndicesRequestConverters::rollover, options,
+    public Cancellable rolloverAsync(RolloverRequest rolloverRequest, RequestOptions options, ActionListener<RolloverResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(rolloverRequest, IndicesRequestConverters::rollover, options,
                 RolloverResponse::fromXContent, listener, emptySet());
     }
 
@@ -995,11 +1036,13 @@ public final class IndicesClient {
      * @deprecated This method uses deprecated request and response objects.
      * The method {@link #rolloverAsync(RolloverRequest, RequestOptions, ActionListener)} should be used instead, which
      * accepts a new request object.
+     * @return cancellable that may be used to cancel the request
      */
     @Deprecated
-    public void rolloverAsync(org.elasticsearch.action.admin.indices.rollover.RolloverRequest rolloverRequest,
-            RequestOptions options, ActionListener<org.elasticsearch.action.admin.indices.rollover.RolloverResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(rolloverRequest, IndicesRequestConverters::rollover, options,
+    public Cancellable rolloverAsync(org.elasticsearch.action.admin.indices.rollover.RolloverRequest rolloverRequest,
+            RequestOptions options,
+            ActionListener<org.elasticsearch.action.admin.indices.rollover.RolloverResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(rolloverRequest, IndicesRequestConverters::rollover, options,
             org.elasticsearch.action.admin.indices.rollover.RolloverResponse::fromXContent, listener, emptySet());
     }
 
@@ -1024,10 +1067,13 @@ public final class IndicesClient {
      * @param getAliasesRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getAliasAsync(GetAliasesRequest getAliasesRequest, RequestOptions options, ActionListener<GetAliasesResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(getAliasesRequest, IndicesRequestConverters::getAlias, options,
-                GetAliasesResponse::fromXContent, listener, singleton(RestStatus.NOT_FOUND.getStatus()));
+    public Cancellable getAliasAsync(GetAliasesRequest getAliasesRequest, RequestOptions options,
+                                     ActionListener<GetAliasesResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(getAliasesRequest,
+            IndicesRequestConverters::getAlias, options,
+            GetAliasesResponse::fromXContent, listener, singleton(RestStatus.NOT_FOUND.getStatus()));
     }
 
     /**
@@ -1051,10 +1097,12 @@ public final class IndicesClient {
      * @param updateSettingsRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putSettingsAsync(UpdateSettingsRequest updateSettingsRequest, RequestOptions options,
-                                 ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(updateSettingsRequest, IndicesRequestConverters::indexPutSettings, options,
+    public Cancellable putSettingsAsync(UpdateSettingsRequest updateSettingsRequest, RequestOptions options,
+                                        ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(updateSettingsRequest,
+            IndicesRequestConverters::indexPutSettings, options,
             AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -1088,11 +1136,14 @@ public final class IndicesClient {
      * @deprecated This old form of request allows types in mappings.
      * Use {@link #putTemplateAsync(PutIndexTemplateRequest, RequestOptions, ActionListener)}
      * instead which introduces a new request object without types.
+     * @return cancellable that may be used to cancel the request
      */
     @Deprecated
-    public void putTemplateAsync(org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest putIndexTemplateRequest,
-            RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(putIndexTemplateRequest, IndicesRequestConverters::putTemplate, options,
+    public Cancellable putTemplateAsync(
+                org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateRequest putIndexTemplateRequest,
+                RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(putIndexTemplateRequest,
+            IndicesRequestConverters::putTemplate, options,
             AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -1120,10 +1171,12 @@ public final class IndicesClient {
      * @param putIndexTemplateRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putTemplateAsync(PutIndexTemplateRequest putIndexTemplateRequest,
-            RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(putIndexTemplateRequest, IndicesRequestConverters::putTemplate, options,
+    public Cancellable putTemplateAsync(PutIndexTemplateRequest putIndexTemplateRequest,
+                                        RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(putIndexTemplateRequest,
+            IndicesRequestConverters::putTemplate, options,
             AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -1137,8 +1190,10 @@ public final class IndicesClient {
      * @return the response
      * @throws IOException in case there is a problem sending the request or parsing back the response
      */
-    public ValidateQueryResponse validateQuery(ValidateQueryRequest validateQueryRequest, RequestOptions options) throws IOException {
-        return restHighLevelClient.performRequestAndParseEntity(validateQueryRequest, IndicesRequestConverters::validateQuery, options,
+    public ValidateQueryResponse validateQuery(ValidateQueryRequest validateQueryRequest,
+                                               RequestOptions options) throws IOException {
+        return restHighLevelClient.performRequestAndParseEntity(validateQueryRequest,
+            IndicesRequestConverters::validateQuery, options,
             ValidateQueryResponse::fromXContent, emptySet());
     }
 
@@ -1150,10 +1205,12 @@ public final class IndicesClient {
      * @param validateQueryRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void validateQueryAsync(ValidateQueryRequest validateQueryRequest, RequestOptions options,
-                                   ActionListener<ValidateQueryResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(validateQueryRequest, IndicesRequestConverters::validateQuery, options,
+    public Cancellable validateQueryAsync(ValidateQueryRequest validateQueryRequest, RequestOptions options,
+                                          ActionListener<ValidateQueryResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(validateQueryRequest,
+            IndicesRequestConverters::validateQuery, options,
             ValidateQueryResponse::fromXContent, listener, emptySet());
     }
 
@@ -1174,7 +1231,8 @@ public final class IndicesClient {
             GetIndexTemplatesRequest getIndexTemplatesRequest, RequestOptions options) throws IOException {
         return restHighLevelClient.performRequestAndParseEntity(getIndexTemplatesRequest,
             IndicesRequestConverters::getTemplatesWithDocumentTypes,
-            options, org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse::fromXContent, emptySet());
+            options,
+            org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse::fromXContent, emptySet());
     }
 
     /**
@@ -1186,8 +1244,8 @@ public final class IndicesClient {
      * @return the response
      * @throws IOException in case there is a problem sending the request or parsing back the response
      */
-    public GetIndexTemplatesResponse getIndexTemplate(GetIndexTemplatesRequest getIndexTemplatesRequest, RequestOptions options)
-                                                  throws IOException {
+    public GetIndexTemplatesResponse getIndexTemplate(GetIndexTemplatesRequest getIndexTemplatesRequest,
+                  RequestOptions options) throws IOException {
         return restHighLevelClient.performRequestAndParseEntity(getIndexTemplatesRequest,
             IndicesRequestConverters::getTemplates,
             options, GetIndexTemplatesResponse::fromXContent, emptySet());
@@ -1203,13 +1261,16 @@ public final class IndicesClient {
      * @param listener the listener to be notified upon request completion
      * @deprecated This method uses an old response object which still refers to types, a deprecated feature. Use
      * {@link #getIndexTemplateAsync(GetIndexTemplatesRequest, RequestOptions, ActionListener)} instead which returns a new response object
+     * @return cancellable that may be used to cancel the request
      */
     @Deprecated
-    public void getTemplateAsync(GetIndexTemplatesRequest getIndexTemplatesRequest, RequestOptions options,
-                                 ActionListener<org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(getIndexTemplatesRequest,
+    public Cancellable getTemplateAsync(GetIndexTemplatesRequest getIndexTemplatesRequest, RequestOptions options,
+            ActionListener<org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(getIndexTemplatesRequest,
             IndicesRequestConverters::getTemplatesWithDocumentTypes,
-            options, org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse::fromXContent, listener, emptySet());
+            options,
+            org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse::fromXContent,
+            listener, emptySet());
     }
 
     /**
@@ -1219,10 +1280,11 @@ public final class IndicesClient {
      * @param getIndexTemplatesRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getIndexTemplateAsync(GetIndexTemplatesRequest getIndexTemplatesRequest, RequestOptions options,
-                                 ActionListener<GetIndexTemplatesResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(getIndexTemplatesRequest,
+    public Cancellable getIndexTemplateAsync(GetIndexTemplatesRequest getIndexTemplatesRequest, RequestOptions options,
+                                             ActionListener<GetIndexTemplatesResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(getIndexTemplatesRequest,
             IndicesRequestConverters::getTemplates,
             options, GetIndexTemplatesResponse::fromXContent, listener, emptySet());
     }
@@ -1235,24 +1297,26 @@ public final class IndicesClient {
      * @return true if any index templates in the request exist, false otherwise
      * @throws IOException in case there is a problem sending the request or parsing back the response
      */
-    public boolean existsTemplate(IndexTemplatesExistRequest indexTemplatesRequest, RequestOptions options) throws IOException {
-        return restHighLevelClient.performRequest(indexTemplatesRequest, IndicesRequestConverters::templatesExist, options,
+    public boolean existsTemplate(IndexTemplatesExistRequest indexTemplatesRequest,
+                                  RequestOptions options) throws IOException {
+        return restHighLevelClient.performRequest(indexTemplatesRequest,
+            IndicesRequestConverters::templatesExist, options,
             RestHighLevelClient::convertExistsResponse, emptySet());
     }
 
     /**
      * Uses the Index Templates API to determine if index templates exist
-     *
      * @param indexTemplatesExistRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion. The listener will be called with the value {@code true}
-     *                 if any index templates in the request exist, false otherwise
+     * @return cancellable that may be used to cancel the request
      */
-    public void existsTemplateAsync(IndexTemplatesExistRequest indexTemplatesExistRequest,
-                                    RequestOptions options,
-                                    ActionListener<Boolean> listener) {
+    public Cancellable existsTemplateAsync(IndexTemplatesExistRequest indexTemplatesExistRequest,
+                                           RequestOptions options,
+                                           ActionListener<Boolean> listener) {
 
-        restHighLevelClient.performRequestAsync(indexTemplatesExistRequest, IndicesRequestConverters::templatesExist, options,
+        return restHighLevelClient.performRequestAsync(indexTemplatesExistRequest,
+            IndicesRequestConverters::templatesExist, options,
             RestHighLevelClient::convertExistsResponse, listener, emptySet());
     }
 
@@ -1273,14 +1337,14 @@ public final class IndicesClient {
      * Asynchronously calls the analyze API
      *
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-analyze.html">Analyze API on elastic.co</a>
-     *
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void analyzeAsync(AnalyzeRequest request, RequestOptions options,
-                             ActionListener<AnalyzeResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, IndicesRequestConverters::analyze, options,
+    public Cancellable analyzeAsync(AnalyzeRequest request, RequestOptions options,
+                                    ActionListener<AnalyzeResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, IndicesRequestConverters::analyze, options,
             AnalyzeResponse::fromXContent, listener, emptySet());
     }
 
@@ -1297,13 +1361,14 @@ public final class IndicesClient {
 
     /**
      * Asynchronously calls the _freeze API
-     *
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void freezeAsync(FreezeIndexRequest request, RequestOptions options, ActionListener<ShardsAcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, IndicesRequestConverters::freezeIndex, options,
+    public Cancellable freezeAsync(FreezeIndexRequest request, RequestOptions options,
+                                   ActionListener<ShardsAcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, IndicesRequestConverters::freezeIndex, options,
             ShardsAcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -1320,13 +1385,15 @@ public final class IndicesClient {
 
     /**
      * Asynchronously calls the _unfreeze API
-     *
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void unfreezeAsync(UnfreezeIndexRequest request, RequestOptions options, ActionListener<ShardsAcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, IndicesRequestConverters::unfreezeIndex, options,
+    public Cancellable unfreezeAsync(UnfreezeIndexRequest request, RequestOptions options,
+                                     ActionListener<ShardsAcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
+            IndicesRequestConverters::unfreezeIndex, options,
             ShardsAcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -1348,14 +1415,14 @@ public final class IndicesClient {
      * Asynchronously delete an index template using the Index Templates API
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-templates.html"> Index Templates API
      * on elastic.co</a>
-     *
      * @param request  the request
      * @param options  the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteTemplateAsync(DeleteIndexTemplateRequest request, RequestOptions options,
-                                    ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, IndicesRequestConverters::deleteTemplate,
+    public Cancellable deleteTemplateAsync(DeleteIndexTemplateRequest request, RequestOptions options,
+                                           ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, IndicesRequestConverters::deleteTemplate,
             options, AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -1372,14 +1439,14 @@ public final class IndicesClient {
 
     /**
      * Asynchronously calls the _reload_search_analyzers API
-     *
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void reloadAnalyzersAsync(ReloadAnalyzersRequest request, RequestOptions options,
-            ActionListener<ReloadAnalyzersResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, IndicesRequestConverters::reloadAnalyzers, options,
+    public Cancellable reloadAnalyzersAsync(ReloadAnalyzersRequest request, RequestOptions options,
+                                            ActionListener<ReloadAnalyzersResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, IndicesRequestConverters::reloadAnalyzers, options,
                 ReloadAnalyzersResponse::fromXContent, listener, emptySet());
     }
 }

+ 16 - 10
client/rest-high-level/src/main/java/org/elasticsearch/client/IngestClient.java

@@ -67,9 +67,10 @@ public final class IngestClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putPipelineAsync(PutPipelineRequest request, RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity( request, IngestRequestConverters::putPipeline, options,
+    public Cancellable putPipelineAsync(PutPipelineRequest request, RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity( request, IngestRequestConverters::putPipeline, options,
             AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -94,9 +95,10 @@ public final class IngestClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getPipelineAsync(GetPipelineRequest request, RequestOptions options, ActionListener<GetPipelineResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity( request, IngestRequestConverters::getPipeline, options,
+    public Cancellable getPipelineAsync(GetPipelineRequest request, RequestOptions options, ActionListener<GetPipelineResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity( request, IngestRequestConverters::getPipeline, options,
             GetPipelineResponse::fromXContent, listener, Collections.singleton(404));
     }
 
@@ -123,9 +125,12 @@ public final class IngestClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deletePipelineAsync(DeletePipelineRequest request, RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity( request, IngestRequestConverters::deletePipeline, options,
+    public Cancellable deletePipelineAsync(DeletePipelineRequest request, RequestOptions options,
+                                           ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity( request,
+            IngestRequestConverters::deletePipeline, options,
             AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -154,11 +159,12 @@ public final class IngestClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void simulateAsync(SimulatePipelineRequest request,
-                              RequestOptions options,
-                              ActionListener<SimulatePipelineResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity( request, IngestRequestConverters::simulatePipeline, options,
+    public Cancellable simulateAsync(SimulatePipelineRequest request,
+                                     RequestOptions options,
+                                     ActionListener<SimulatePipelineResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity( request, IngestRequestConverters::simulatePipeline, options,
             SimulatePipelineResponse::fromXContent, listener, emptySet());
     }
 }

+ 20 - 13
client/rest-high-level/src/main/java/org/elasticsearch/client/LicenseClient.java

@@ -80,9 +80,10 @@ public final class LicenseClient {
      * Asynchronously updates license for the cluster.
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putLicenseAsync(PutLicenseRequest request, RequestOptions options, ActionListener<PutLicenseResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, LicenseRequestConverters::putLicense, options,
+    public Cancellable putLicenseAsync(PutLicenseRequest request, RequestOptions options, ActionListener<PutLicenseResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, LicenseRequestConverters::putLicense, options,
             PutLicenseResponse::fromXContent, listener, emptySet());
     }
 
@@ -101,9 +102,10 @@ public final class LicenseClient {
      * Asynchronously returns the current license for the cluster cluster.
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getLicenseAsync(GetLicenseRequest request, RequestOptions options, ActionListener<GetLicenseResponse> listener) {
-        restHighLevelClient.performRequestAsync(request, LicenseRequestConverters::getLicense, options,
+    public Cancellable getLicenseAsync(GetLicenseRequest request, RequestOptions options, ActionListener<GetLicenseResponse> listener) {
+        return restHighLevelClient.performRequestAsync(request, LicenseRequestConverters::getLicense, options,
             response -> new GetLicenseResponse(convertResponseToJson(response)), listener, emptySet());
     }
 
@@ -122,9 +124,12 @@ public final class LicenseClient {
      * Asynchronously deletes license from the cluster.
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteLicenseAsync(DeleteLicenseRequest request, RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, LicenseRequestConverters::deleteLicense, options,
+    public Cancellable deleteLicenseAsync(DeleteLicenseRequest request, RequestOptions options,
+                                          ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
+            LicenseRequestConverters::deleteLicense, options,
             AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -143,12 +148,13 @@ public final class LicenseClient {
      * Asynchronously starts a trial license on the cluster.
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void startTrialAsync(StartTrialRequest request,
-                                RequestOptions options,
-                                ActionListener<StartTrialResponse> listener) {
+    public Cancellable startTrialAsync(StartTrialRequest request,
+                                       RequestOptions options,
+                                       ActionListener<StartTrialResponse> listener) {
 
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, LicenseRequestConverters::startTrial, options,
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, LicenseRequestConverters::startTrial, options,
             StartTrialResponse::fromXContent, listener, singleton(403));
     }
 
@@ -167,10 +173,11 @@ public final class LicenseClient {
      * Asynchronously initiates an indefinite basic license.
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void startBasicAsync(StartBasicRequest request, RequestOptions options,
-                                ActionListener<StartBasicResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, LicenseRequestConverters::startBasic, options,
+    public Cancellable startBasicAsync(StartBasicRequest request, RequestOptions options,
+                                       ActionListener<StartBasicResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, LicenseRequestConverters::startBasic, options,
             StartBasicResponse::fromXContent, listener, emptySet());
     }
 

+ 197 - 137
client/rest-high-level/src/main/java/org/elasticsearch/client/MachineLearningClient.java

@@ -153,13 +153,13 @@ public final class MachineLearningClient {
      * <p>
      * For additional info
      * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-put-job.html">ML PUT job documentation</a>
-     *
      * @param request  The request containing the {@link org.elasticsearch.client.ml.job.config.Job} settings
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putJobAsync(PutJobRequest request, RequestOptions options, ActionListener<PutJobResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable putJobAsync(PutJobRequest request, RequestOptions options, ActionListener<PutJobResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::putJob,
                 options,
                 PutJobResponse::fromXContent,
@@ -192,13 +192,13 @@ public final class MachineLearningClient {
      * <p>
      * For additional info
      * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-get-job.html">ML GET job documentation</a>
-     *
      * @param request  {@link GetJobRequest} Request containing a list of jobId(s) and additional options
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified with {@link GetJobResponse} upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getJobAsync(GetJobRequest request, RequestOptions options, ActionListener<GetJobResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getJobAsync(GetJobRequest request, RequestOptions options, ActionListener<GetJobResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::getJob,
                 options,
                 GetJobResponse::fromXContent,
@@ -231,13 +231,13 @@ public final class MachineLearningClient {
      * <p>
      * For additional info
      * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-get-job-stats.html">Get job stats docs</a>
-     *
      * @param request  {@link GetJobStatsRequest} Request containing a list of jobId(s) and additional options
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified with {@link GetJobStatsResponse} upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getJobStatsAsync(GetJobStatsRequest request, RequestOptions options, ActionListener<GetJobStatsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getJobStatsAsync(GetJobStatsRequest request, RequestOptions options, ActionListener<GetJobStatsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::getJobStats,
                 options,
                 GetJobStatsResponse::fromXContent,
@@ -272,14 +272,14 @@ public final class MachineLearningClient {
      * For additional info
      * see <a href="http://www.elastic.co/guide/en/elasticsearch/reference/current/ml-delete-expired-data.html">ML Delete Expired Data
      * documentation</a>
-     *
      * @param request  The request to delete expired ML data
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteExpiredDataAsync(DeleteExpiredDataRequest request, RequestOptions options,
-                               ActionListener<DeleteExpiredDataResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable deleteExpiredDataAsync(DeleteExpiredDataRequest request, RequestOptions options,
+                                              ActionListener<DeleteExpiredDataResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::deleteExpiredData,
             options,
             DeleteExpiredDataResponse::fromXContent,
@@ -313,12 +313,13 @@ public final class MachineLearningClient {
      * For additional info
      * see <a href="http://www.elastic.co/guide/en/elasticsearch/reference/current/ml-delete-job.html">ML Delete Job documentation</a>
      *
-     * @param request  The request to delete the job
+     *  @param request  The request to delete the job
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteJobAsync(DeleteJobRequest request, RequestOptions options, ActionListener<DeleteJobResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable deleteJobAsync(DeleteJobRequest request, RequestOptions options, ActionListener<DeleteJobResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::deleteJob,
             options,
             DeleteJobResponse::fromXContent,
@@ -360,9 +361,10 @@ public final class MachineLearningClient {
      * @param request  Request containing job_id and additional optional options
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void openJobAsync(OpenJobRequest request, RequestOptions options, ActionListener<OpenJobResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable openJobAsync(OpenJobRequest request, RequestOptions options, ActionListener<OpenJobResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::openJob,
                 options,
                 OpenJobResponse::fromXContent,
@@ -400,9 +402,10 @@ public final class MachineLearningClient {
      * @param request  Request containing job_ids and additional options. See {@link CloseJobRequest}
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void closeJobAsync(CloseJobRequest request, RequestOptions options, ActionListener<CloseJobResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable closeJobAsync(CloseJobRequest request, RequestOptions options, ActionListener<CloseJobResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::closeJob,
                 options,
                 CloseJobResponse::fromXContent,
@@ -449,9 +452,10 @@ public final class MachineLearningClient {
      * @param request  The {@link FlushJobRequest} object enclosing the `jobId` and additional request options
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void flushJobAsync(FlushJobRequest request, RequestOptions options, ActionListener<FlushJobResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable flushJobAsync(FlushJobRequest request, RequestOptions options, ActionListener<FlushJobResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::flushJob,
                 options,
                 FlushJobResponse::fromXContent,
@@ -489,9 +493,10 @@ public final class MachineLearningClient {
      * @param request  ForecastJobRequest with forecasting options
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void forecastJobAsync(ForecastJobRequest request, RequestOptions options, ActionListener<ForecastJobResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable forecastJobAsync(ForecastJobRequest request, RequestOptions options, ActionListener<ForecastJobResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::forecastJob,
                 options,
                 ForecastJobResponse::fromXContent,
@@ -529,9 +534,11 @@ public final class MachineLearningClient {
      * @param request  the {@link DeleteForecastRequest} object enclosing the desired jobId, forecastIDs, and other options
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteForecastAsync(DeleteForecastRequest request, RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable deleteForecastAsync(DeleteForecastRequest request, RequestOptions options,
+                                           ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::deleteForecast,
                 options,
                 AcknowledgedResponse::fromXContent,
@@ -569,10 +576,11 @@ public final class MachineLearningClient {
      * @param request The request to delete the model snapshot
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteModelSnapshotAsync(DeleteModelSnapshotRequest request, RequestOptions options,
-                                         ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable deleteModelSnapshotAsync(DeleteModelSnapshotRequest request, RequestOptions options,
+                                                ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::deleteModelSnapshot,
             options,
             AcknowledgedResponse::fromXContent,
@@ -610,10 +618,11 @@ public final class MachineLearningClient {
      * @param request The request to revert to a previous model snapshot
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void revertModelSnapshotAsync(RevertModelSnapshotRequest request, RequestOptions options,
-                                         ActionListener<RevertModelSnapshotResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable revertModelSnapshotAsync(RevertModelSnapshotRequest request, RequestOptions options,
+                                                ActionListener<RevertModelSnapshotResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::revertModelSnapshot,
             options,
             RevertModelSnapshotResponse::fromXContent,
@@ -649,9 +658,10 @@ public final class MachineLearningClient {
      * @param request The request containing the {@link org.elasticsearch.client.ml.datafeed.DatafeedConfig} settings
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putDatafeedAsync(PutDatafeedRequest request, RequestOptions options, ActionListener<PutDatafeedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable putDatafeedAsync(PutDatafeedRequest request, RequestOptions options, ActionListener<PutDatafeedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::putDatafeed,
                 options,
                 PutDatafeedResponse::fromXContent,
@@ -689,9 +699,11 @@ public final class MachineLearningClient {
      * @param request The request containing the {@link org.elasticsearch.client.ml.datafeed.DatafeedUpdate} settings
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void updateDatafeedAsync(UpdateDatafeedRequest request, RequestOptions options, ActionListener<PutDatafeedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable updateDatafeedAsync(UpdateDatafeedRequest request, RequestOptions options,
+                                           ActionListener<PutDatafeedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::updateDatafeed,
             options,
             PutDatafeedResponse::fromXContent,
@@ -730,9 +742,11 @@ public final class MachineLearningClient {
      * @param request {@link GetDatafeedRequest} Request containing a list of datafeedId(s) and additional options
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified with {@link GetDatafeedResponse} upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getDatafeedAsync(GetDatafeedRequest request, RequestOptions options, ActionListener<GetDatafeedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getDatafeedAsync(GetDatafeedRequest request, RequestOptions options,
+                                        ActionListener<GetDatafeedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::getDatafeed,
                 options,
                 GetDatafeedResponse::fromXContent,
@@ -770,9 +784,11 @@ public final class MachineLearningClient {
      * @param request The request to delete the datafeed
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteDatafeedAsync(DeleteDatafeedRequest request, RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable deleteDatafeedAsync(DeleteDatafeedRequest request, RequestOptions options,
+                                           ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::deleteDatafeed,
                 options,
                 AcknowledgedResponse::fromXContent,
@@ -810,9 +826,11 @@ public final class MachineLearningClient {
      * @param request The request to start the datafeed
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void startDatafeedAsync(StartDatafeedRequest request, RequestOptions options, ActionListener<StartDatafeedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable startDatafeedAsync(StartDatafeedRequest request, RequestOptions options,
+                                          ActionListener<StartDatafeedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::startDatafeed,
             options,
             StartDatafeedResponse::fromXContent,
@@ -850,9 +868,11 @@ public final class MachineLearningClient {
      * @param request The request to stop the datafeed
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void stopDatafeedAsync(StopDatafeedRequest request, RequestOptions options, ActionListener<StopDatafeedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable stopDatafeedAsync(StopDatafeedRequest request, RequestOptions options,
+                                         ActionListener<StopDatafeedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::stopDatafeed,
             options,
             StopDatafeedResponse::fromXContent,
@@ -910,11 +930,12 @@ public final class MachineLearningClient {
      * @param request  {@link GetDatafeedStatsRequest} Request containing a list of datafeedId(s) and additional options
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified with {@link GetDatafeedStatsResponse} upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getDatafeedStatsAsync(GetDatafeedStatsRequest request,
-                                      RequestOptions options,
-                                      ActionListener<GetDatafeedStatsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getDatafeedStatsAsync(GetDatafeedStatsRequest request,
+                                             RequestOptions options,
+                                             ActionListener<GetDatafeedStatsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::getDatafeedStats,
             options,
             GetDatafeedStatsResponse::fromXContent,
@@ -932,11 +953,12 @@ public final class MachineLearningClient {
      * @param request The request to preview the datafeed
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void previewDatafeedAsync(PreviewDatafeedRequest request,
-                                     RequestOptions options,
-                                     ActionListener<PreviewDatafeedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable previewDatafeedAsync(PreviewDatafeedRequest request,
+                                            RequestOptions options,
+                                            ActionListener<PreviewDatafeedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::previewDatafeed,
             options,
             PreviewDatafeedResponse::fromXContent,
@@ -972,9 +994,10 @@ public final class MachineLearningClient {
      * @param request  the {@link UpdateJobRequest} object enclosing the desired updates
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void updateJobAsync(UpdateJobRequest request, RequestOptions options, ActionListener<PutJobResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable updateJobAsync(UpdateJobRequest request, RequestOptions options, ActionListener<PutJobResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::updateJob,
                 options,
                 PutJobResponse::fromXContent,
@@ -1005,12 +1028,13 @@ public final class MachineLearningClient {
      * For additional info
      * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/ml-get-bucket.html">ML GET buckets documentation</a>
      *
-     * @param request  The request
+     *  @param request  The request
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getBucketsAsync(GetBucketsRequest request, RequestOptions options, ActionListener<GetBucketsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getBucketsAsync(GetBucketsRequest request, RequestOptions options, ActionListener<GetBucketsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::getBuckets,
                 options,
                 GetBucketsResponse::fromXContent,
@@ -1047,9 +1071,11 @@ public final class MachineLearningClient {
      * @param request  The request
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getCategoriesAsync(GetCategoriesRequest request, RequestOptions options, ActionListener<GetCategoriesResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getCategoriesAsync(GetCategoriesRequest request, RequestOptions options,
+                                          ActionListener<GetCategoriesResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::getCategories,
                 options,
                 GetCategoriesResponse::fromXContent,
@@ -1086,10 +1112,11 @@ public final class MachineLearningClient {
      * @param request  The request
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getModelSnapshotsAsync(GetModelSnapshotsRequest request, RequestOptions options,
-                                       ActionListener<GetModelSnapshotsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getModelSnapshotsAsync(GetModelSnapshotsRequest request, RequestOptions options,
+                                              ActionListener<GetModelSnapshotsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::getModelSnapshots,
             options,
             GetModelSnapshotsResponse::fromXContent,
@@ -1127,10 +1154,11 @@ public final class MachineLearningClient {
      * @param request  The request
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void updateModelSnapshotAsync(UpdateModelSnapshotRequest request, RequestOptions options,
-                                       ActionListener<UpdateModelSnapshotResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable updateModelSnapshotAsync(UpdateModelSnapshotRequest request, RequestOptions options,
+                                                ActionListener<UpdateModelSnapshotResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::updateModelSnapshot,
             options,
             UpdateModelSnapshotResponse::fromXContent,
@@ -1166,10 +1194,11 @@ public final class MachineLearningClient {
      * @param request  The request
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getOverallBucketsAsync(GetOverallBucketsRequest request, RequestOptions options,
-                                       ActionListener<GetOverallBucketsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getOverallBucketsAsync(GetOverallBucketsRequest request, RequestOptions options,
+                                              ActionListener<GetOverallBucketsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::getOverallBuckets,
                 options,
                 GetOverallBucketsResponse::fromXContent,
@@ -1203,9 +1232,10 @@ public final class MachineLearningClient {
      * @param request  the request
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getRecordsAsync(GetRecordsRequest request, RequestOptions options, ActionListener<GetRecordsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getRecordsAsync(GetRecordsRequest request, RequestOptions options, ActionListener<GetRecordsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::getRecords,
                 options,
                 GetRecordsResponse::fromXContent,
@@ -1245,9 +1275,10 @@ public final class MachineLearningClient {
      * @param request  PostDataRequest containing the data to post and some additional options
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void postDataAsync(PostDataRequest request, RequestOptions options, ActionListener<PostDataResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable postDataAsync(PostDataRequest request, RequestOptions options, ActionListener<PostDataResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::postData,
                 options,
                 PostDataResponse::fromXContent,
@@ -1283,9 +1314,11 @@ public final class MachineLearningClient {
      * @param request The calendars request
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getCalendarsAsync(GetCalendarsRequest request, RequestOptions options, ActionListener<GetCalendarsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getCalendarsAsync(GetCalendarsRequest request, RequestOptions options,
+                                         ActionListener<GetCalendarsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::getCalendars,
                 options,
                 GetCalendarsResponse::fromXContent,
@@ -1321,10 +1354,11 @@ public final class MachineLearningClient {
      * @param request  the request
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getInfluencersAsync(GetInfluencersRequest request, RequestOptions options,
-                                    ActionListener<GetInfluencersResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getInfluencersAsync(GetInfluencersRequest request, RequestOptions options,
+                                           ActionListener<GetInfluencersResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::getInfluencers,
                 options,
                 GetInfluencersResponse::fromXContent,
@@ -1362,9 +1396,10 @@ public final class MachineLearningClient {
      * @param request  The request
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putCalendarAsync(PutCalendarRequest request, RequestOptions options, ActionListener<PutCalendarResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable putCalendarAsync(PutCalendarRequest request, RequestOptions options, ActionListener<PutCalendarResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::putCalendar,
                 options,
                 PutCalendarResponse::fromXContent,
@@ -1402,9 +1437,11 @@ public final class MachineLearningClient {
      * @param request  The request
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putCalendarJobAsync(PutCalendarJobRequest request, RequestOptions options, ActionListener<PutCalendarResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable putCalendarJobAsync(PutCalendarJobRequest request, RequestOptions options,
+                                           ActionListener<PutCalendarResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::putCalendarJob,
             options,
             PutCalendarResponse::fromXContent,
@@ -1442,11 +1479,12 @@ public final class MachineLearningClient {
      * @param request  The request
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteCalendarJobAsync(DeleteCalendarJobRequest request,
-                                       RequestOptions options,
-                                       ActionListener<PutCalendarResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable deleteCalendarJobAsync(DeleteCalendarJobRequest request,
+                                              RequestOptions options,
+                                              ActionListener<PutCalendarResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::deleteCalendarJob,
             options,
             PutCalendarResponse::fromXContent,
@@ -1484,9 +1522,11 @@ public final class MachineLearningClient {
      * @param request  The request to delete the calendar
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteCalendarAsync(DeleteCalendarRequest request, RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable deleteCalendarAsync(DeleteCalendarRequest request, RequestOptions options,
+                                           ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
                 MLRequestConverters::deleteCalendar,
                 options,
                 AcknowledgedResponse::fromXContent,
@@ -1524,10 +1564,11 @@ public final class MachineLearningClient {
      * @param request  The request
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getCalendarEventsAsync(GetCalendarEventsRequest request, RequestOptions options,
-                                       ActionListener<GetCalendarEventsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getCalendarEventsAsync(GetCalendarEventsRequest request, RequestOptions options,
+                                              ActionListener<GetCalendarEventsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::getCalendarEvents,
             options,
             GetCalendarEventsResponse::fromXContent,
@@ -1565,10 +1606,11 @@ public final class MachineLearningClient {
      * @param request  The request
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void postCalendarEventAsync(PostCalendarEventRequest request, RequestOptions options,
-                                       ActionListener<PostCalendarEventResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable postCalendarEventAsync(PostCalendarEventRequest request, RequestOptions options,
+                                              ActionListener<PostCalendarEventResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::postCalendarEvents,
             options,
             PostCalendarEventResponse::fromXContent,
@@ -1606,11 +1648,12 @@ public final class MachineLearningClient {
      * @param request  The request
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteCalendarEventAsync(DeleteCalendarEventRequest request,
-                                         RequestOptions options,
-                                         ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable deleteCalendarEventAsync(DeleteCalendarEventRequest request,
+                                                RequestOptions options,
+                                                ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::deleteCalendarEvent,
             options,
             AcknowledgedResponse::fromXContent,
@@ -1646,9 +1689,10 @@ public final class MachineLearningClient {
      * @param request  The request containing the {@link org.elasticsearch.client.ml.job.config.MlFilter} settings
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putFilterAsync(PutFilterRequest request, RequestOptions options, ActionListener<PutFilterResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable putFilterAsync(PutFilterRequest request, RequestOptions options, ActionListener<PutFilterResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::putFilter,
             options,
             PutFilterResponse::fromXContent,
@@ -1684,9 +1728,10 @@ public final class MachineLearningClient {
      * @param request  The request
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getFilterAsync(GetFiltersRequest request, RequestOptions options, ActionListener<GetFiltersResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getFilterAsync(GetFiltersRequest request, RequestOptions options, ActionListener<GetFiltersResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::getFilter,
             options,
             GetFiltersResponse::fromXContent,
@@ -1724,9 +1769,10 @@ public final class MachineLearningClient {
      * @param request  The request
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void updateFilterAsync(UpdateFilterRequest request, RequestOptions options, ActionListener<PutFilterResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable updateFilterAsync(UpdateFilterRequest request, RequestOptions options, ActionListener<PutFilterResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::updateFilter,
             options,
             PutFilterResponse::fromXContent,
@@ -1764,9 +1810,11 @@ public final class MachineLearningClient {
      * @param request The request to delete the filter
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteFilterAsync(DeleteFilterRequest request, RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable deleteFilterAsync(DeleteFilterRequest request, RequestOptions options,
+                                         ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::deleteFilter,
             options,
             AcknowledgedResponse::fromXContent,
@@ -1802,9 +1850,10 @@ public final class MachineLearningClient {
      * @param request The request of Machine Learning info
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getMlInfoAsync(MlInfoRequest request, RequestOptions options, ActionListener<MlInfoResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getMlInfoAsync(MlInfoRequest request, RequestOptions options, ActionListener<MlInfoResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::mlInfo,
             options,
             MlInfoResponse::fromXContent,
@@ -1842,10 +1891,11 @@ public final class MachineLearningClient {
      * @param request The find file structure request
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void findFileStructureAsync(FindFileStructureRequest request, RequestOptions options,
-                                       ActionListener<FindFileStructureResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable findFileStructureAsync(FindFileStructureRequest request, RequestOptions options,
+                                              ActionListener<FindFileStructureResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::findFileStructure,
             options,
             FindFileStructureResponse::fromXContent,
@@ -1881,9 +1931,11 @@ public final class MachineLearningClient {
      * @param request The request of Machine Learning info
      * @param options  Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void setUpgradeModeAsync(SetUpgradeModeRequest request, RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable setUpgradeModeAsync(SetUpgradeModeRequest request, RequestOptions options,
+                                           ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::setUpgradeMode,
             options,
             AcknowledgedResponse::fromXContent,
@@ -1925,10 +1977,11 @@ public final class MachineLearningClient {
      * {@link org.elasticsearch.client.ml.dataframe.DataFrameAnalyticsConfig}
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putDataFrameAnalyticsAsync(PutDataFrameAnalyticsRequest request, RequestOptions options,
-                                           ActionListener<PutDataFrameAnalyticsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable putDataFrameAnalyticsAsync(PutDataFrameAnalyticsRequest request, RequestOptions options,
+                                                  ActionListener<PutDataFrameAnalyticsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::putDataFrameAnalytics,
             options,
             PutDataFrameAnalyticsResponse::fromXContent,
@@ -1967,10 +2020,11 @@ public final class MachineLearningClient {
      * @param request The {@link GetDataFrameAnalyticsRequest}
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getDataFrameAnalyticsAsync(GetDataFrameAnalyticsRequest request, RequestOptions options,
-                                           ActionListener<GetDataFrameAnalyticsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getDataFrameAnalyticsAsync(GetDataFrameAnalyticsRequest request, RequestOptions options,
+                                                  ActionListener<GetDataFrameAnalyticsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::getDataFrameAnalytics,
             options,
             GetDataFrameAnalyticsResponse::fromXContent,
@@ -2008,10 +2062,11 @@ public final class MachineLearningClient {
      * @param request The {@link GetDataFrameAnalyticsStatsRequest}
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getDataFrameAnalyticsStatsAsync(GetDataFrameAnalyticsStatsRequest request, RequestOptions options,
-                                                ActionListener<GetDataFrameAnalyticsStatsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getDataFrameAnalyticsStatsAsync(GetDataFrameAnalyticsStatsRequest request, RequestOptions options,
+                                                       ActionListener<GetDataFrameAnalyticsStatsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::getDataFrameAnalyticsStats,
             options,
             GetDataFrameAnalyticsStatsResponse::fromXContent,
@@ -2047,13 +2102,14 @@ public final class MachineLearningClient {
      * see <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/start-dfanalytics.html">
      *     Start Data Frame Analytics documentation</a>
      *
-     * @param request The {@link StartDataFrameAnalyticsRequest}
+     *  @param request The {@link StartDataFrameAnalyticsRequest}
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void startDataFrameAnalyticsAsync(StartDataFrameAnalyticsRequest request, RequestOptions options,
-                                             ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable startDataFrameAnalyticsAsync(StartDataFrameAnalyticsRequest request, RequestOptions options,
+                                                    ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::startDataFrameAnalytics,
             options,
             AcknowledgedResponse::fromXContent,
@@ -2092,10 +2148,11 @@ public final class MachineLearningClient {
      * @param request The {@link StopDataFrameAnalyticsRequest}
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void stopDataFrameAnalyticsAsync(StopDataFrameAnalyticsRequest request, RequestOptions options,
-                                            ActionListener<StopDataFrameAnalyticsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable stopDataFrameAnalyticsAsync(StopDataFrameAnalyticsRequest request, RequestOptions options,
+                                                   ActionListener<StopDataFrameAnalyticsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::stopDataFrameAnalytics,
             options,
             StopDataFrameAnalyticsResponse::fromXContent,
@@ -2134,10 +2191,11 @@ public final class MachineLearningClient {
      * @param request The {@link DeleteDataFrameAnalyticsRequest}
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteDataFrameAnalyticsAsync(DeleteDataFrameAnalyticsRequest request, RequestOptions options,
-                                              ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable deleteDataFrameAnalyticsAsync(DeleteDataFrameAnalyticsRequest request, RequestOptions options,
+                                                     ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::deleteDataFrameAnalytics,
             options,
             AcknowledgedResponse::fromXContent,
@@ -2176,10 +2234,11 @@ public final class MachineLearningClient {
      * @param request The {@link EvaluateDataFrameRequest}
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void evaluateDataFrameAsync(EvaluateDataFrameRequest request, RequestOptions options,
-                                       ActionListener<EvaluateDataFrameResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable evaluateDataFrameAsync(EvaluateDataFrameRequest request, RequestOptions options,
+                                              ActionListener<EvaluateDataFrameResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             MLRequestConverters::evaluateDataFrame,
             options,
             EvaluateDataFrameResponse::fromXContent,
@@ -2219,10 +2278,11 @@ public final class MachineLearningClient {
      * @param request The {@link PutDataFrameAnalyticsRequest}
      * @param options Additional request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener Listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void estimateMemoryUsageAsync(PutDataFrameAnalyticsRequest request, RequestOptions options,
-                                         ActionListener<EstimateMemoryUsageResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(
+    public Cancellable estimateMemoryUsageAsync(PutDataFrameAnalyticsRequest request, RequestOptions options,
+                                                ActionListener<EstimateMemoryUsageResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
             request,
             MLRequestConverters::estimateMemoryUsage,
             options,

+ 4 - 3
client/rest-high-level/src/main/java/org/elasticsearch/client/MigrationClient.java

@@ -58,10 +58,11 @@ public final class MigrationClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getDeprecationInfoAsync(DeprecationInfoRequest request, RequestOptions options,
-                                        ActionListener<DeprecationInfoResponse> listener)  {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, MigrationRequestConverters::getDeprecationInfo, options,
+    public Cancellable getDeprecationInfoAsync(DeprecationInfoRequest request, RequestOptions options,
+                                               ActionListener<DeprecationInfoResponse> listener)  {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, MigrationRequestConverters::getDeprecationInfo, options,
             DeprecationInfoResponse::fromXContent, listener, Collections.emptySet());
     }
 }

+ 177 - 131
client/rest-high-level/src/main/java/org/elasticsearch/client/RestHighLevelClient.java

@@ -503,9 +503,11 @@ public class RestHighLevelClient implements Closeable {
      * @param bulkRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void bulkAsync(BulkRequest bulkRequest, RequestOptions options, ActionListener<BulkResponse> listener) {
-        performRequestAsyncAndParseEntity(bulkRequest, RequestConverters::bulk, options, BulkResponse::fromXContent, listener, emptySet());
+    public final Cancellable bulkAsync(BulkRequest bulkRequest, RequestOptions options, ActionListener<BulkResponse> listener) {
+        return performRequestAsyncAndParseEntity(bulkRequest, RequestConverters::bulk, options,
+            BulkResponse::fromXContent, listener, emptySet());
     }
 
     /**
@@ -540,9 +542,11 @@ public class RestHighLevelClient implements Closeable {
      * @param reindexRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void reindexAsync(ReindexRequest reindexRequest, RequestOptions options, ActionListener<BulkByScrollResponse> listener) {
-        performRequestAsyncAndParseEntity(
+    public final Cancellable reindexAsync(ReindexRequest reindexRequest, RequestOptions options,
+                                          ActionListener<BulkByScrollResponse> listener) {
+        return performRequestAsyncAndParseEntity(
             reindexRequest, RequestConverters::reindex, options, BulkByScrollResponse::fromXContent, listener, singleton(409)
         );
     }
@@ -568,10 +572,11 @@ public class RestHighLevelClient implements Closeable {
      * @param updateByQueryRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void updateByQueryAsync(UpdateByQueryRequest updateByQueryRequest, RequestOptions options,
-                                         ActionListener<BulkByScrollResponse> listener) {
-        performRequestAsyncAndParseEntity(
+    public final Cancellable updateByQueryAsync(UpdateByQueryRequest updateByQueryRequest, RequestOptions options,
+                                                ActionListener<BulkByScrollResponse> listener) {
+        return performRequestAsyncAndParseEntity(
             updateByQueryRequest, RequestConverters::updateByQuery, options, BulkByScrollResponse::fromXContent, listener, singleton(409)
         );
     }
@@ -597,10 +602,11 @@ public class RestHighLevelClient implements Closeable {
      * @param deleteByQueryRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void deleteByQueryAsync(DeleteByQueryRequest deleteByQueryRequest, RequestOptions options,
-                                         ActionListener<BulkByScrollResponse> listener) {
-        performRequestAsyncAndParseEntity(
+    public final Cancellable deleteByQueryAsync(DeleteByQueryRequest deleteByQueryRequest, RequestOptions options,
+                                                ActionListener<BulkByScrollResponse> listener) {
+        return performRequestAsyncAndParseEntity(
             deleteByQueryRequest, RequestConverters::deleteByQuery, options, BulkByScrollResponse::fromXContent, listener, singleton(409)
         );
     }
@@ -625,10 +631,11 @@ public class RestHighLevelClient implements Closeable {
      * @param rethrottleRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void deleteByQueryRethrottleAsync(RethrottleRequest rethrottleRequest, RequestOptions options,
-            ActionListener<ListTasksResponse> listener) {
-        performRequestAsyncAndParseEntity(rethrottleRequest, RequestConverters::rethrottleDeleteByQuery, options,
+    public final Cancellable deleteByQueryRethrottleAsync(RethrottleRequest rethrottleRequest, RequestOptions options,
+                                                          ActionListener<ListTasksResponse> listener) {
+        return performRequestAsyncAndParseEntity(rethrottleRequest, RequestConverters::rethrottleDeleteByQuery, options,
                 ListTasksResponse::fromXContent, listener, emptySet());
     }
 
@@ -652,10 +659,11 @@ public class RestHighLevelClient implements Closeable {
      * @param rethrottleRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void updateByQueryRethrottleAsync(RethrottleRequest rethrottleRequest, RequestOptions options,
-            ActionListener<ListTasksResponse> listener) {
-        performRequestAsyncAndParseEntity(rethrottleRequest, RequestConverters::rethrottleUpdateByQuery, options,
+    public final Cancellable updateByQueryRethrottleAsync(RethrottleRequest rethrottleRequest, RequestOptions options,
+                                                          ActionListener<ListTasksResponse> listener) {
+        return performRequestAsyncAndParseEntity(rethrottleRequest, RequestConverters::rethrottleUpdateByQuery, options,
                 ListTasksResponse::fromXContent, listener, emptySet());
     }
 
@@ -677,15 +685,15 @@ public class RestHighLevelClient implements Closeable {
      * Executes a reindex rethrottling request.
      * See the <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-reindex.html#docs-reindex-rethrottle">
      * Reindex rethrottling API on elastic.co</a>
-     *
      * @param rethrottleRequest the request
      * @param options           the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener          the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void reindexRethrottleAsync(RethrottleRequest rethrottleRequest, RequestOptions options,
-            ActionListener<ListTasksResponse> listener) {
-        performRequestAsyncAndParseEntity(rethrottleRequest, RequestConverters::rethrottleReindex, options, ListTasksResponse::fromXContent,
-                listener, emptySet());
+    public final Cancellable reindexRethrottleAsync(RethrottleRequest rethrottleRequest, RequestOptions options,
+                                                    ActionListener<ListTasksResponse> listener) {
+        return performRequestAsyncAndParseEntity(rethrottleRequest,
+            RequestConverters::rethrottleReindex, options, ListTasksResponse::fromXContent, listener, emptySet());
     }
 
     /**
@@ -725,9 +733,10 @@ public class RestHighLevelClient implements Closeable {
      * @param getRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void getAsync(GetRequest getRequest, RequestOptions options, ActionListener<GetResponse> listener) {
-        performRequestAsyncAndParseEntity(getRequest, RequestConverters::get, options, GetResponse::fromXContent, listener,
+    public final Cancellable getAsync(GetRequest getRequest, RequestOptions options, ActionListener<GetResponse> listener) {
+        return performRequestAsyncAndParseEntity(getRequest, RequestConverters::get, options, GetResponse::fromXContent, listener,
                 singleton(404));
     }
 
@@ -764,10 +773,12 @@ public class RestHighLevelClient implements Closeable {
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
      * @deprecated use {@link #mgetAsync(MultiGetRequest, RequestOptions, ActionListener)} instead
+     * @return cancellable that may be used to cancel the request
      */
     @Deprecated
-    public final void multiGetAsync(MultiGetRequest multiGetRequest, RequestOptions options, ActionListener<MultiGetResponse> listener) {
-        mgetAsync(multiGetRequest, options, listener);
+    public final Cancellable multiGetAsync(MultiGetRequest multiGetRequest, RequestOptions options,
+                                           ActionListener<MultiGetResponse> listener) {
+        return mgetAsync(multiGetRequest, options, listener);
     }
 
     /**
@@ -776,10 +787,12 @@ public class RestHighLevelClient implements Closeable {
      * @param multiGetRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void mgetAsync(MultiGetRequest multiGetRequest, RequestOptions options, ActionListener<MultiGetResponse> listener) {
-        performRequestAsyncAndParseEntity(multiGetRequest, RequestConverters::multiGet, options, MultiGetResponse::fromXContent, listener,
-                singleton(404));
+    public final Cancellable mgetAsync(MultiGetRequest multiGetRequest, RequestOptions options,
+                                       ActionListener<MultiGetResponse> listener) {
+        return performRequestAsyncAndParseEntity(multiGetRequest, RequestConverters::multiGet, options,
+            MultiGetResponse::fromXContent, listener, singleton(404));
     }
 
     /**
@@ -799,9 +812,10 @@ public class RestHighLevelClient implements Closeable {
      * @param getRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void existsAsync(GetRequest getRequest, RequestOptions options, ActionListener<Boolean> listener) {
-        performRequestAsync(getRequest, RequestConverters::exists, options, RestHighLevelClient::convertExistsResponse, listener,
+    public final Cancellable existsAsync(GetRequest getRequest, RequestOptions options, ActionListener<Boolean> listener) {
+        return performRequestAsync(getRequest, RequestConverters::exists, options, RestHighLevelClient::convertExistsResponse, listener,
                 emptySet());
     }
 
@@ -824,10 +838,11 @@ public class RestHighLevelClient implements Closeable {
      * @param getRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void existsSourceAsync(GetRequest getRequest, RequestOptions options, ActionListener<Boolean> listener) {
-        performRequestAsync(getRequest, RequestConverters::sourceExists, options, RestHighLevelClient::convertExistsResponse, listener,
-                emptySet());
+    public final Cancellable existsSourceAsync(GetRequest getRequest, RequestOptions options, ActionListener<Boolean> listener) {
+        return performRequestAsync(getRequest, RequestConverters::sourceExists, options,
+            RestHighLevelClient::convertExistsResponse, listener, emptySet());
     }
 
     /**
@@ -838,7 +853,8 @@ public class RestHighLevelClient implements Closeable {
      * @return the response
      */
     public final IndexResponse index(IndexRequest indexRequest, RequestOptions options) throws IOException {
-        return performRequestAndParseEntity(indexRequest, RequestConverters::index, options, IndexResponse::fromXContent, emptySet());
+        return performRequestAndParseEntity(indexRequest, RequestConverters::index, options,
+            IndexResponse::fromXContent, emptySet());
     }
 
     /**
@@ -847,9 +863,10 @@ public class RestHighLevelClient implements Closeable {
      * @param indexRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void indexAsync(IndexRequest indexRequest, RequestOptions options, ActionListener<IndexResponse> listener) {
-        performRequestAsyncAndParseEntity(indexRequest, RequestConverters::index, options, IndexResponse::fromXContent, listener,
+    public final Cancellable indexAsync(IndexRequest indexRequest, RequestOptions options, ActionListener<IndexResponse> listener) {
+        return performRequestAsyncAndParseEntity(indexRequest, RequestConverters::index, options, IndexResponse::fromXContent, listener,
                 emptySet());
     }
 
@@ -871,9 +888,10 @@ public class RestHighLevelClient implements Closeable {
      * @param countRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void countAsync(CountRequest countRequest, RequestOptions options, ActionListener<CountResponse> listener) {
-        performRequestAsyncAndParseEntity(countRequest, RequestConverters::count,  options,CountResponse::fromXContent,
+    public final Cancellable countAsync(CountRequest countRequest, RequestOptions options, ActionListener<CountResponse> listener) {
+        return performRequestAsyncAndParseEntity(countRequest, RequestConverters::count,  options,CountResponse::fromXContent,
             listener, emptySet());
     }
 
@@ -894,9 +912,10 @@ public class RestHighLevelClient implements Closeable {
      * @param updateRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void updateAsync(UpdateRequest updateRequest, RequestOptions options, ActionListener<UpdateResponse> listener) {
-        performRequestAsyncAndParseEntity(updateRequest, RequestConverters::update, options, UpdateResponse::fromXContent, listener,
+    public final Cancellable updateAsync(UpdateRequest updateRequest, RequestOptions options, ActionListener<UpdateResponse> listener) {
+        return performRequestAsyncAndParseEntity(updateRequest, RequestConverters::update, options, UpdateResponse::fromXContent, listener,
                 emptySet());
     }
 
@@ -918,9 +937,10 @@ public class RestHighLevelClient implements Closeable {
      * @param deleteRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void deleteAsync(DeleteRequest deleteRequest, RequestOptions options, ActionListener<DeleteResponse> listener) {
-        performRequestAsyncAndParseEntity(deleteRequest, RequestConverters::delete, options, DeleteResponse::fromXContent, listener,
+    public final Cancellable deleteAsync(DeleteRequest deleteRequest, RequestOptions options, ActionListener<DeleteResponse> listener) {
+        return performRequestAsyncAndParseEntity(deleteRequest, RequestConverters::delete, options, DeleteResponse::fromXContent, listener,
             Collections.singleton(404));
     }
 
@@ -946,9 +966,10 @@ public class RestHighLevelClient implements Closeable {
      * @param searchRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void searchAsync(SearchRequest searchRequest, RequestOptions options, ActionListener<SearchResponse> listener) {
-        performRequestAsyncAndParseEntity(
+    public final Cancellable searchAsync(SearchRequest searchRequest, RequestOptions options, ActionListener<SearchResponse> listener) {
+        return performRequestAsyncAndParseEntity(
                 searchRequest,
                 r -> RequestConverters.search(r, "_search"),
                 options,
@@ -992,11 +1013,12 @@ public class RestHighLevelClient implements Closeable {
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
      * @deprecated use {@link #msearchAsync(MultiSearchRequest, RequestOptions, ActionListener)} instead
+     * @return cancellable that may be used to cancel the request
      */
     @Deprecated
-    public final void multiSearchAsync(MultiSearchRequest searchRequest, RequestOptions options,
-                                   ActionListener<MultiSearchResponse> listener) {
-        msearchAsync(searchRequest, options, listener);
+    public final Cancellable multiSearchAsync(MultiSearchRequest searchRequest, RequestOptions options,
+                                              ActionListener<MultiSearchResponse> listener) {
+        return msearchAsync(searchRequest, options, listener);
     }
 
     /**
@@ -1006,10 +1028,11 @@ public class RestHighLevelClient implements Closeable {
      * @param searchRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void msearchAsync(MultiSearchRequest searchRequest, RequestOptions options,
-                                   ActionListener<MultiSearchResponse> listener) {
-        performRequestAsyncAndParseEntity(searchRequest, RequestConverters::multiSearch, options, MultiSearchResponse::fromXContext,
+    public final Cancellable msearchAsync(MultiSearchRequest searchRequest, RequestOptions options,
+                                          ActionListener<MultiSearchResponse> listener) {
+        return performRequestAsyncAndParseEntity(searchRequest, RequestConverters::multiSearch, options, MultiSearchResponse::fromXContext,
                 listener, emptySet());
     }
 
@@ -1051,11 +1074,12 @@ public class RestHighLevelClient implements Closeable {
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
      * @deprecated use {@link #scrollAsync(SearchScrollRequest, RequestOptions, ActionListener)} instead
+     * @return cancellable that may be used to cancel the request
      */
     @Deprecated
-    public final void searchScrollAsync(SearchScrollRequest searchScrollRequest, RequestOptions options,
-                                  ActionListener<SearchResponse> listener) {
-        scrollAsync(searchScrollRequest, options, listener);
+    public final Cancellable searchScrollAsync(SearchScrollRequest searchScrollRequest, RequestOptions options,
+                                               ActionListener<SearchResponse> listener) {
+        return scrollAsync(searchScrollRequest, options, listener);
     }
 
     /**
@@ -1066,11 +1090,12 @@ public class RestHighLevelClient implements Closeable {
      * @param searchScrollRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void scrollAsync(SearchScrollRequest searchScrollRequest, RequestOptions options,
-                                  ActionListener<SearchResponse> listener) {
-        performRequestAsyncAndParseEntity(searchScrollRequest, RequestConverters::searchScroll, options, SearchResponse::fromXContent,
-                listener, emptySet());
+    public final Cancellable scrollAsync(SearchScrollRequest searchScrollRequest, RequestOptions options,
+                                         ActionListener<SearchResponse> listener) {
+        return performRequestAsyncAndParseEntity(searchScrollRequest, RequestConverters::searchScroll,
+            options, SearchResponse::fromXContent, listener, emptySet());
     }
 
     /**
@@ -1095,11 +1120,12 @@ public class RestHighLevelClient implements Closeable {
      * @param clearScrollRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void clearScrollAsync(ClearScrollRequest clearScrollRequest, RequestOptions options,
-                                       ActionListener<ClearScrollResponse> listener) {
-        performRequestAsyncAndParseEntity(clearScrollRequest, RequestConverters::clearScroll, options, ClearScrollResponse::fromXContent,
-                listener, emptySet());
+    public final Cancellable clearScrollAsync(ClearScrollRequest clearScrollRequest, RequestOptions options,
+                                              ActionListener<ClearScrollResponse> listener) {
+        return performRequestAsyncAndParseEntity(clearScrollRequest, RequestConverters::clearScroll,
+            options, ClearScrollResponse::fromXContent, listener, emptySet());
     }
 
     /**
@@ -1121,10 +1147,11 @@ public class RestHighLevelClient implements Closeable {
      *
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/search-template.html">Search Template API
      * on elastic.co</a>.
+     * @return cancellable that may be used to cancel the request
      */
-    public final void searchTemplateAsync(SearchTemplateRequest searchTemplateRequest, RequestOptions options,
-                                          ActionListener<SearchTemplateResponse> listener) {
-        performRequestAsyncAndParseEntity(searchTemplateRequest, RequestConverters::searchTemplate, options,
+    public final Cancellable searchTemplateAsync(SearchTemplateRequest searchTemplateRequest, RequestOptions options,
+                                                 ActionListener<SearchTemplateResponse> listener) {
+        return performRequestAsyncAndParseEntity(searchTemplateRequest, RequestConverters::searchTemplate, options,
             SearchTemplateResponse::fromXContent, listener, emptySet());
     }
 
@@ -1152,9 +1179,10 @@ public class RestHighLevelClient implements Closeable {
      * @param explainRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void explainAsync(ExplainRequest explainRequest, RequestOptions options, ActionListener<ExplainResponse> listener) {
-        performRequestAsync(explainRequest, RequestConverters::explain, options,
+    public final Cancellable explainAsync(ExplainRequest explainRequest, RequestOptions options, ActionListener<ExplainResponse> listener) {
+        return performRequestAsync(explainRequest, RequestConverters::explain, options,
             response -> {
                 CheckedFunction<XContentParser, ExplainResponse, IOException> entityParser =
                     parser -> ExplainResponse.fromXContent(parser, convertExistsResponse(response));
@@ -1186,9 +1214,12 @@ public class RestHighLevelClient implements Closeable {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void termvectorsAsync(TermVectorsRequest request, RequestOptions options, ActionListener<TermVectorsResponse> listener) {
-        performRequestAsyncAndParseEntity(request, RequestConverters::termVectors, options, TermVectorsResponse::fromXContent, listener,
+    public final Cancellable termvectorsAsync(TermVectorsRequest request, RequestOptions options,
+                                              ActionListener<TermVectorsResponse> listener) {
+        return performRequestAsyncAndParseEntity(request, RequestConverters::termVectors, options,
+            TermVectorsResponse::fromXContent, listener,
             emptySet());
     }
 
@@ -1216,10 +1247,11 @@ public class RestHighLevelClient implements Closeable {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void mtermvectorsAsync(MultiTermVectorsRequest request, RequestOptions options,
-            ActionListener<MultiTermVectorsResponse> listener) {
-        performRequestAsyncAndParseEntity(
+    public final Cancellable mtermvectorsAsync(MultiTermVectorsRequest request, RequestOptions options,
+                                               ActionListener<MultiTermVectorsResponse> listener) {
+        return performRequestAsyncAndParseEntity(
             request, RequestConverters::mtermVectors, options, MultiTermVectorsResponse::fromXContent, listener, emptySet());
     }
 
@@ -1255,11 +1287,12 @@ public class RestHighLevelClient implements Closeable {
      *
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/multi-search-template.html">Multi Search Template API
      * on elastic.co</a>.
+     * @return cancellable that may be used to cancel the request
      */
-    public final void msearchTemplateAsync(MultiSearchTemplateRequest multiSearchTemplateRequest,
-                                           RequestOptions options,
-                                           ActionListener<MultiSearchTemplateResponse> listener) {
-        performRequestAsyncAndParseEntity(multiSearchTemplateRequest, RequestConverters::multiSearchTemplate,
+    public final Cancellable msearchTemplateAsync(MultiSearchTemplateRequest multiSearchTemplateRequest,
+                                                  RequestOptions options,
+                                                  ActionListener<MultiSearchTemplateResponse> listener) {
+        return performRequestAsyncAndParseEntity(multiSearchTemplateRequest, RequestConverters::multiSearchTemplate,
             options, MultiSearchTemplateResponse::fromXContext, listener, emptySet());
     }
 
@@ -1270,9 +1303,12 @@ public class RestHighLevelClient implements Closeable {
      * @param rankEvalRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void rankEvalAsync(RankEvalRequest rankEvalRequest, RequestOptions options,  ActionListener<RankEvalResponse> listener) {
-        performRequestAsyncAndParseEntity(rankEvalRequest, RequestConverters::rankEval, options,  RankEvalResponse::fromXContent, listener,
+    public final Cancellable rankEvalAsync(RankEvalRequest rankEvalRequest, RequestOptions options,
+                                           ActionListener<RankEvalResponse> listener) {
+        return performRequestAsyncAndParseEntity(rankEvalRequest, RequestConverters::rankEval, options,
+            RankEvalResponse::fromXContent, listener,
                 emptySet());
     }
 
@@ -1310,10 +1346,11 @@ public class RestHighLevelClient implements Closeable {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getScriptAsync(GetStoredScriptRequest request, RequestOptions options,
-                               ActionListener<GetStoredScriptResponse> listener) {
-        performRequestAsyncAndParseEntity(request, RequestConverters::getScript, options,
+    public Cancellable getScriptAsync(GetStoredScriptRequest request, RequestOptions options,
+                                      ActionListener<GetStoredScriptResponse> listener) {
+        return performRequestAsyncAndParseEntity(request, RequestConverters::getScript, options,
             GetStoredScriptResponse::fromXContent, listener, emptySet());
     }
 
@@ -1337,10 +1374,11 @@ public class RestHighLevelClient implements Closeable {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteScriptAsync(DeleteStoredScriptRequest request, RequestOptions options,
-                                  ActionListener<AcknowledgedResponse> listener) {
-        performRequestAsyncAndParseEntity(request, RequestConverters::deleteScript, options,
+    public Cancellable deleteScriptAsync(DeleteStoredScriptRequest request, RequestOptions options,
+                                         ActionListener<AcknowledgedResponse> listener) {
+        return performRequestAsyncAndParseEntity(request, RequestConverters::deleteScript, options,
             AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -1365,10 +1403,11 @@ public class RestHighLevelClient implements Closeable {
      * @param putStoredScriptRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putScriptAsync(PutStoredScriptRequest putStoredScriptRequest, RequestOptions options,
-                               ActionListener<AcknowledgedResponse> listener) {
-        performRequestAsyncAndParseEntity(putStoredScriptRequest, RequestConverters::putScript, options,
+    public Cancellable putScriptAsync(PutStoredScriptRequest putStoredScriptRequest, RequestOptions options,
+                                      ActionListener<AcknowledgedResponse> listener) {
+        return performRequestAsyncAndParseEntity(putStoredScriptRequest, RequestConverters::putScript, options,
             AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -1379,10 +1418,11 @@ public class RestHighLevelClient implements Closeable {
      * @param fieldCapabilitiesRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public final void fieldCapsAsync(FieldCapabilitiesRequest fieldCapabilitiesRequest, RequestOptions options,
-                                     ActionListener<FieldCapabilitiesResponse> listener) {
-        performRequestAsyncAndParseEntity(fieldCapabilitiesRequest, RequestConverters::fieldCaps, options,
+    public final Cancellable fieldCapsAsync(FieldCapabilitiesRequest fieldCapabilitiesRequest, RequestOptions options,
+                                            ActionListener<FieldCapabilitiesResponse> listener) {
+        return performRequestAsyncAndParseEntity(fieldCapabilitiesRequest, RequestConverters::fieldCaps, options,
             FieldCapabilitiesResponse::fromXContent, listener, emptySet());
     }
 
@@ -1514,26 +1554,28 @@ public class RestHighLevelClient implements Closeable {
     /**
      * @deprecated If creating a new HLRC ReST API call, consider creating new actions instead of reusing server actions. The Validation
      * layer has been added to the ReST client, and requests should extend {@link Validatable} instead of {@link ActionRequest}.
+     * @return Cancellable instance that may be used to cancel the request
      */
     @Deprecated
-    protected final <Req extends ActionRequest, Resp> void performRequestAsyncAndParseEntity(Req request,
-                                                                         CheckedFunction<Req, Request, IOException> requestConverter,
-                                                                         RequestOptions options,
-                                                                         CheckedFunction<XContentParser, Resp, IOException> entityParser,
-                                                                         ActionListener<Resp> listener, Set<Integer> ignores) {
-        performRequestAsync(request, requestConverter, options,
+    protected final <Req extends ActionRequest, Resp> Cancellable performRequestAsyncAndParseEntity(Req request,
+                CheckedFunction<Req, Request, IOException> requestConverter,
+                RequestOptions options,
+                CheckedFunction<XContentParser, Resp, IOException> entityParser,
+                ActionListener<Resp> listener, Set<Integer> ignores) {
+        return performRequestAsync(request, requestConverter, options,
                 response -> parseEntity(response.getEntity(), entityParser), listener, ignores);
     }
 
     /**
      * Defines a helper method for asynchronously performing a request.
-     */
-    protected final <Req extends Validatable, Resp> void performRequestAsyncAndParseEntity(Req request,
-                                                                       CheckedFunction<Req, Request, IOException> requestConverter,
-                                                                       RequestOptions options,
-                                                                       CheckedFunction<XContentParser, Resp, IOException> entityParser,
-                                                                       ActionListener<Resp> listener, Set<Integer> ignores) {
-        performRequestAsync(request, requestConverter, options,
+     * @return Cancellable instance that may be used to cancel the request
+     */
+    protected final <Req extends Validatable, Resp> Cancellable performRequestAsyncAndParseEntity(Req request,
+                CheckedFunction<Req, Request, IOException> requestConverter,
+                RequestOptions options,
+                CheckedFunction<XContentParser, Resp, IOException> entityParser,
+                ActionListener<Resp> listener, Set<Integer> ignores) {
+        return performRequestAsync(request, requestConverter, options,
                 response -> parseEntity(response.getEntity(), entityParser), listener, ignores);
     }
 
@@ -1541,56 +1583,59 @@ public class RestHighLevelClient implements Closeable {
     /**
      * @deprecated If creating a new HLRC ReST API call, consider creating new actions instead of reusing server actions. The Validation
      * layer has been added to the ReST client, and requests should extend {@link Validatable} instead of {@link ActionRequest}.
+     * @return Cancellable instance that may be used to cancel the request
      */
     @Deprecated
-    protected final <Req extends ActionRequest, Resp> void performRequestAsync(Req request,
-                                                                            CheckedFunction<Req, Request, IOException> requestConverter,
-                                                                            RequestOptions options,
-                                                                            CheckedFunction<Response, Resp, IOException> responseConverter,
-                                                                            ActionListener<Resp> listener, Set<Integer> ignores) {
+    protected final <Req extends ActionRequest, Resp> Cancellable performRequestAsync(Req request,
+                CheckedFunction<Req, Request, IOException> requestConverter,
+                RequestOptions options,
+                CheckedFunction<Response, Resp, IOException> responseConverter,
+                ActionListener<Resp> listener, Set<Integer> ignores) {
         ActionRequestValidationException validationException = request.validate();
         if (validationException != null && validationException.validationErrors().isEmpty() == false) {
             listener.onFailure(validationException);
-            return;
+            return Cancellable.NO_OP;
         }
-        internalPerformRequestAsync(request, requestConverter, options, responseConverter, listener, ignores);
+        return internalPerformRequestAsync(request, requestConverter, options, responseConverter, listener, ignores);
     }
 
     /**
      * Defines a helper method for asynchronously performing a request.
+     * @return Cancellable instance that may be used to cancel the request
      */
-    protected final <Req extends Validatable, Resp> void performRequestAsync(Req request,
-                                                                          CheckedFunction<Req, Request, IOException> requestConverter,
-                                                                          RequestOptions options,
-                                                                          CheckedFunction<Response, Resp, IOException> responseConverter,
-                                                                          ActionListener<Resp> listener, Set<Integer> ignores) {
+    protected final <Req extends Validatable, Resp> Cancellable performRequestAsync(Req request,
+                CheckedFunction<Req, Request, IOException> requestConverter,
+                RequestOptions options,
+                CheckedFunction<Response, Resp, IOException> responseConverter,
+                ActionListener<Resp> listener, Set<Integer> ignores) {
         Optional<ValidationException> validationException = request.validate();
         if (validationException != null && validationException.isPresent()) {
             listener.onFailure(validationException.get());
-            return;
+            return Cancellable.NO_OP;
         }
-        internalPerformRequestAsync(request, requestConverter, options, responseConverter, listener, ignores);
+        return internalPerformRequestAsync(request, requestConverter, options, responseConverter, listener, ignores);
     }
 
     /**
      * Provides common functionality for asynchronously performing a request.
+     * @return Cancellable instance that may be used to cancel the request
      */
-    private <Req, Resp> void internalPerformRequestAsync(Req request,
-                                                 CheckedFunction<Req, Request, IOException> requestConverter,
-                                                 RequestOptions options,
-                                                 CheckedFunction<Response, Resp, IOException> responseConverter,
-                                                 ActionListener<Resp> listener, Set<Integer> ignores) {
+    private <Req, Resp> Cancellable internalPerformRequestAsync(Req request,
+                 CheckedFunction<Req, Request, IOException> requestConverter,
+                 RequestOptions options,
+                 CheckedFunction<Response, Resp, IOException> responseConverter,
+                 ActionListener<Resp> listener, Set<Integer> ignores) {
         Request req;
         try {
             req = requestConverter.apply(request);
         } catch (Exception e) {
             listener.onFailure(e);
-            return;
+            return Cancellable.NO_OP;
         }
         req.setOptions(options);
 
         ResponseListener responseListener = wrapResponseListener(responseConverter, listener, ignores);
-        client.performRequestAsync(req, responseListener);
+        return client.performRequestAsync(req, responseListener);
     }
 
 
@@ -1634,28 +1679,29 @@ public class RestHighLevelClient implements Closeable {
 
     /**
      * Asynchronous request which returns empty {@link Optional}s in the case of 404s or parses entity into an Optional
+     * @return Cancellable instance that may be used to cancel the request
      */
-    protected final <Req extends Validatable, Resp> void performRequestAsyncAndParseOptionalEntity(Req request,
-            CheckedFunction<Req, Request, IOException> requestConverter,
-            RequestOptions options,
-            CheckedFunction<XContentParser, Resp, IOException> entityParser,
-            ActionListener<Optional<Resp>> listener) {
+    protected final <Req extends Validatable, Resp> Cancellable performRequestAsyncAndParseOptionalEntity(Req request,
+              CheckedFunction<Req, Request, IOException> requestConverter,
+              RequestOptions options,
+              CheckedFunction<XContentParser, Resp, IOException> entityParser,
+              ActionListener<Optional<Resp>> listener) {
         Optional<ValidationException> validationException = request.validate();
         if (validationException != null && validationException.isPresent()) {
             listener.onFailure(validationException.get());
-            return;
+            return Cancellable.NO_OP;
         }
         Request req;
         try {
             req = requestConverter.apply(request);
         } catch (Exception e) {
             listener.onFailure(e);
-            return;
+            return Cancellable.NO_OP;
         }
         req.setOptions(options);
         ResponseListener responseListener = wrapResponseListener404sOptional(response -> parseEntity(response.getEntity(),
                 entityParser), listener);
-        client.performRequestAsync(req, responseListener);
+        return client.performRequestAsync(req, responseListener);
     }
 
     final <Resp> ResponseListener wrapResponseListener404sOptional(CheckedFunction<Response, Resp, IOException> responseConverter,

+ 32 - 26
client/rest-high-level/src/main/java/org/elasticsearch/client/RollupClient.java

@@ -30,8 +30,6 @@ import org.elasticsearch.client.rollup.GetRollupJobRequest;
 import org.elasticsearch.client.rollup.GetRollupJobResponse;
 import org.elasticsearch.client.rollup.GetRollupCapsRequest;
 import org.elasticsearch.client.rollup.GetRollupCapsResponse;
-import org.elasticsearch.client.rollup.GetRollupJobRequest;
-import org.elasticsearch.client.rollup.GetRollupJobResponse;
 import org.elasticsearch.client.rollup.PutRollupJobRequest;
 import org.elasticsearch.client.rollup.StartRollupJobRequest;
 import org.elasticsearch.client.rollup.StartRollupJobResponse;
@@ -80,9 +78,11 @@ public class RollupClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putRollupJobAsync(PutRollupJobRequest request, RequestOptions options, ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable putRollupJobAsync(PutRollupJobRequest request, RequestOptions options,
+                                         ActionListener<AcknowledgedResponse> listener) {
+       return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             RollupRequestConverters::putJob,
             options,
             AcknowledgedResponse::fromXContent,
@@ -113,10 +113,11 @@ public class RollupClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void startRollupJobAsync(StartRollupJobRequest request, RequestOptions options,
-            ActionListener<StartRollupJobResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable startRollupJobAsync(StartRollupJobRequest request, RequestOptions options,
+                                           ActionListener<StartRollupJobResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             RollupRequestConverters::startJob,
             options,
             StartRollupJobResponse::fromXContent,
@@ -147,10 +148,11 @@ public class RollupClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void stopRollupJobAsync(StopRollupJobRequest request, RequestOptions options,
-            ActionListener<StopRollupJobResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable stopRollupJobAsync(StopRollupJobRequest request, RequestOptions options,
+                                          ActionListener<StopRollupJobResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             RollupRequestConverters::stopJob,
             options,
             StopRollupJobResponse::fromXContent,
@@ -180,11 +182,12 @@ public class RollupClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteRollupJobAsync(DeleteRollupJobRequest request,
-                                     RequestOptions options,
-                                     ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable deleteRollupJobAsync(DeleteRollupJobRequest request,
+                                            RequestOptions options,
+                                            ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             RollupRequestConverters::deleteJob,
             options,
             AcknowledgedResponse::fromXContent,
@@ -215,11 +218,11 @@ public class RollupClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-
-
-    public void getRollupJobAsync(GetRollupJobRequest request, RequestOptions options, ActionListener<GetRollupJobResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getRollupJobAsync(GetRollupJobRequest request, RequestOptions options,
+                                         ActionListener<GetRollupJobResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             RollupRequestConverters::getJob,
             options,
             GetRollupJobResponse::fromXContent,
@@ -251,9 +254,10 @@ public class RollupClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void searchAsync(SearchRequest request, RequestOptions options, ActionListener<SearchResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(
+    public Cancellable searchAsync(SearchRequest request, RequestOptions options, ActionListener<SearchResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
                 request,
                 RollupRequestConverters::search,
                 options,
@@ -286,10 +290,11 @@ public class RollupClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getRollupCapabilitiesAsync(GetRollupCapsRequest request, RequestOptions options,
-                                           ActionListener<GetRollupCapsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getRollupCapabilitiesAsync(GetRollupCapsRequest request, RequestOptions options,
+                                                  ActionListener<GetRollupCapsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             RollupRequestConverters::getRollupCaps,
             options,
             GetRollupCapsResponse::fromXContent,
@@ -322,10 +327,11 @@ public class RollupClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getRollupIndexCapabilitiesAsync(GetRollupIndexCapsRequest request, RequestOptions options,
-                                           ActionListener<GetRollupIndexCapsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request,
+    public Cancellable getRollupIndexCapabilitiesAsync(GetRollupIndexCapsRequest request, RequestOptions options,
+                                                       ActionListener<GetRollupIndexCapsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
             RollupRequestConverters::getRollupIndexCaps,
             options,
             GetRollupIndexCapsResponse::fromXContent,

+ 157 - 84
client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityClient.java

@@ -31,6 +31,8 @@ import org.elasticsearch.client.security.CreateApiKeyRequest;
 import org.elasticsearch.client.security.CreateApiKeyResponse;
 import org.elasticsearch.client.security.CreateTokenRequest;
 import org.elasticsearch.client.security.CreateTokenResponse;
+import org.elasticsearch.client.security.DelegatePkiAuthenticationRequest;
+import org.elasticsearch.client.security.DelegatePkiAuthenticationResponse;
 import org.elasticsearch.client.security.DeletePrivilegesRequest;
 import org.elasticsearch.client.security.DeletePrivilegesResponse;
 import org.elasticsearch.client.security.DeleteRoleMappingRequest;
@@ -111,9 +113,10 @@ public final class SecurityClient {
      * @param request the request with the user's name
      * @param options the request options (e.g., headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getUsersAsync(GetUsersRequest request, RequestOptions options, ActionListener<GetUsersResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::getUsers, options,
+    public Cancellable getUsersAsync(GetUsersRequest request, RequestOptions options, ActionListener<GetUsersResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::getUsers, options,
             GetUsersResponse::fromXContent, listener, emptySet());
     }
 
@@ -140,9 +143,10 @@ public final class SecurityClient {
      * @param request  the request with the user's information
      * @param options  the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putUserAsync(PutUserRequest request, RequestOptions options, ActionListener<PutUserResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::putUser, options,
+    public Cancellable putUserAsync(PutUserRequest request, RequestOptions options, ActionListener<PutUserResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::putUser, options,
             PutUserResponse::fromXContent, listener, emptySet());
     }
 
@@ -167,9 +171,10 @@ public final class SecurityClient {
      * @param request the request with the user to delete
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteUserAsync(DeleteUserRequest request, RequestOptions options, ActionListener<DeleteUserResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::deleteUser, options,
+    public Cancellable deleteUserAsync(DeleteUserRequest request, RequestOptions options, ActionListener<DeleteUserResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::deleteUser, options,
             DeleteUserResponse::fromXContent, listener, singleton(404));
     }
 
@@ -194,10 +199,11 @@ public final class SecurityClient {
      * @param request the request with the role mapping information
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putRoleMappingAsync(final PutRoleMappingRequest request, final RequestOptions options,
-            final ActionListener<PutRoleMappingResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::putRoleMapping, options,
+    public Cancellable putRoleMappingAsync(final PutRoleMappingRequest request, final RequestOptions options,
+                                           final ActionListener<PutRoleMappingResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::putRoleMapping, options,
                 PutRoleMappingResponse::fromXContent, listener, emptySet());
     }
 
@@ -214,7 +220,8 @@ public final class SecurityClient {
      * @throws IOException in case there is a problem sending the request or
      * parsing back the response
      */
-    public GetRoleMappingsResponse getRoleMappings(final GetRoleMappingsRequest request, final RequestOptions options) throws IOException {
+    public GetRoleMappingsResponse getRoleMappings(final GetRoleMappingsRequest request,
+                                                   final RequestOptions options) throws IOException {
         return restHighLevelClient.performRequestAndParseEntity(request, SecurityRequestConverters::getRoleMappings,
             options, GetRoleMappingsResponse::fromXContent, emptySet());
     }
@@ -228,10 +235,11 @@ public final class SecurityClient {
      * If no role mapping name is provided then retrieves all role mappings.
      * @param options  the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getRoleMappingsAsync(final GetRoleMappingsRequest request, final RequestOptions options,
-            final ActionListener<GetRoleMappingsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::getRoleMappings,
+    public Cancellable getRoleMappingsAsync(final GetRoleMappingsRequest request, final RequestOptions options,
+                                            final ActionListener<GetRoleMappingsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::getRoleMappings,
                 options, GetRoleMappingsResponse::fromXContent, listener, emptySet());
     }
 
@@ -274,10 +282,11 @@ public final class SecurityClient {
      * @param request  the request with the user to enable
      * @param options  the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void enableUserAsync(EnableUserRequest request, RequestOptions options,
-                                ActionListener<Boolean> listener) {
-        restHighLevelClient.performRequestAsync(request, SecurityRequestConverters::enableUser, options,
+    public Cancellable enableUserAsync(EnableUserRequest request, RequestOptions options,
+                                       ActionListener<Boolean> listener) {
+        return restHighLevelClient.performRequestAsync(request, SecurityRequestConverters::enableUser, options,
             RestHighLevelClient::convertExistsResponse, listener, emptySet());
     }
 
@@ -290,11 +299,12 @@ public final class SecurityClient {
      * @param request  the request with the user to enable
      * @param listener the listener to be notified upon request completion
      * @deprecated use {@link #enableUserAsync(EnableUserRequest, RequestOptions, ActionListener)} instead
+     * @return cancellable that may be used to cancel the request
      */
     @Deprecated
-    public void enableUserAsync(RequestOptions options, EnableUserRequest request,
-                                ActionListener<Boolean> listener) {
-        enableUserAsync(request, options, listener);
+    public Cancellable enableUserAsync(RequestOptions options, EnableUserRequest request,
+                                       ActionListener<Boolean> listener) {
+        return enableUserAsync(request, options, listener);
     }
 
     /**
@@ -336,10 +346,11 @@ public final class SecurityClient {
      * @param request  the request with the user to disable
      * @param options  the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void disableUserAsync(DisableUserRequest request, RequestOptions options,
-                                 ActionListener<Boolean> listener) {
-        restHighLevelClient.performRequestAsync(request, SecurityRequestConverters::disableUser, options,
+    public Cancellable disableUserAsync(DisableUserRequest request, RequestOptions options,
+                                        ActionListener<Boolean> listener) {
+        return restHighLevelClient.performRequestAsync(request, SecurityRequestConverters::disableUser, options,
             RestHighLevelClient::convertExistsResponse, listener, emptySet());
     }
 
@@ -352,11 +363,12 @@ public final class SecurityClient {
      * @param request  the request with the user to disable
      * @param listener the listener to be notified upon request completion
      * @deprecated use {@link #disableUserAsync(DisableUserRequest, RequestOptions, ActionListener)} instead
+     * @return cancellable that may be used to cancel the request
      */
     @Deprecated
-    public void disableUserAsync(RequestOptions options, DisableUserRequest request,
-                                 ActionListener<Boolean> listener) {
-        disableUserAsync(request, options, listener);
+    public Cancellable disableUserAsync(RequestOptions options, DisableUserRequest request,
+                                        ActionListener<Boolean> listener) {
+        return disableUserAsync(request, options, listener);
     }
 
     /**
@@ -379,9 +391,10 @@ public final class SecurityClient {
      *
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void authenticateAsync(RequestOptions options, ActionListener<AuthenticateResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(AuthenticateRequest.INSTANCE, AuthenticateRequest::getRequest, options,
+    public Cancellable authenticateAsync(RequestOptions options, ActionListener<AuthenticateResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(AuthenticateRequest.INSTANCE, AuthenticateRequest::getRequest, options,
                 AuthenticateResponse::fromXContent, listener, emptySet());
     }
 
@@ -403,13 +416,14 @@ public final class SecurityClient {
      * Asynchronously determine whether the current user has a specified list of privileges
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-has-privileges.html">
      * the docs</a> for more.
-     *
      * @param request the request with the privileges to check
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void hasPrivilegesAsync(HasPrivilegesRequest request, RequestOptions options, ActionListener<HasPrivilegesResponse> listener) {
-         restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::hasPrivileges, options,
+    public Cancellable hasPrivilegesAsync(HasPrivilegesRequest request, RequestOptions options,
+                                          ActionListener<HasPrivilegesResponse> listener) {
+         return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::hasPrivileges, options,
             HasPrivilegesResponse::fromXContent, listener, emptySet());
     }
 
@@ -426,9 +440,11 @@ public final class SecurityClient {
      * Asynchronously retrieve the set of effective privileges held by the current user.
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getUserPrivilegesAsync(RequestOptions options, ActionListener<GetUserPrivilegesResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(GetUserPrivilegesRequest.INSTANCE, GetUserPrivilegesRequest::getRequest,
+    public Cancellable getUserPrivilegesAsync(RequestOptions options, ActionListener<GetUserPrivilegesResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
+            GetUserPrivilegesRequest.INSTANCE, GetUserPrivilegesRequest::getRequest,
             options, GetUserPrivilegesResponse::fromXContent, listener, emptySet());
     }
 
@@ -455,10 +471,11 @@ public final class SecurityClient {
      * @param request  the request with the realm names and usernames to clear the cache for
      * @param options  the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void clearRealmCacheAsync(ClearRealmCacheRequest request, RequestOptions options,
-                                     ActionListener<ClearRealmCacheResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::clearRealmCache, options,
+    public Cancellable clearRealmCacheAsync(ClearRealmCacheRequest request, RequestOptions options,
+                                            ActionListener<ClearRealmCacheResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::clearRealmCache, options,
             ClearRealmCacheResponse::fromXContent, listener, emptySet());
     }
 
@@ -485,10 +502,11 @@ public final class SecurityClient {
      * @param request  the request with the roles for which the cache should be cleared.
      * @param options  the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void clearRolesCacheAsync(ClearRolesCacheRequest request, RequestOptions options,
-                                     ActionListener<ClearRolesCacheResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::clearRolesCache, options,
+    public Cancellable clearRolesCacheAsync(ClearRolesCacheRequest request, RequestOptions options,
+                                            ActionListener<ClearRolesCacheResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::clearRolesCache, options,
             ClearRolesCacheResponse::fromXContent, listener, emptySet());
     }
 
@@ -513,9 +531,11 @@ public final class SecurityClient {
      *
      * @param options  the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getSslCertificatesAsync(RequestOptions options, ActionListener<GetSslCertificatesResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(GetSslCertificatesRequest.INSTANCE, GetSslCertificatesRequest::getRequest,
+    public Cancellable getSslCertificatesAsync(RequestOptions options, ActionListener<GetSslCertificatesResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
+            GetSslCertificatesRequest.INSTANCE, GetSslCertificatesRequest::getRequest,
             options, GetSslCertificatesResponse::fromXContent, listener, emptySet());
     }
 
@@ -558,10 +578,11 @@ public final class SecurityClient {
      * @param request  the request with the user's new password
      * @param options  the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void changePasswordAsync(ChangePasswordRequest request, RequestOptions options,
-                                    ActionListener<Boolean> listener) {
-        restHighLevelClient.performRequestAsync(request, SecurityRequestConverters::changePassword, options,
+    public Cancellable changePasswordAsync(ChangePasswordRequest request, RequestOptions options,
+                                           ActionListener<Boolean> listener) {
+        return restHighLevelClient.performRequestAsync(request, SecurityRequestConverters::changePassword, options,
             RestHighLevelClient::convertExistsResponse, listener, emptySet());
     }
 
@@ -574,11 +595,12 @@ public final class SecurityClient {
      * @param request  the request with the user's new password
      * @param listener the listener to be notified upon request completion
      * @deprecated use {@link #changePasswordAsync(ChangePasswordRequest, RequestOptions, ActionListener)} instead
+     * @return cancellable that may be used to cancel the request
      */
     @Deprecated
-    public void changePasswordAsync(RequestOptions options, ChangePasswordRequest request,
-                                    ActionListener<Boolean> listener) {
-        changePasswordAsync(request, options, listener);
+    public Cancellable changePasswordAsync(RequestOptions options, ChangePasswordRequest request,
+                                           ActionListener<Boolean> listener) {
+        return changePasswordAsync(request, options, listener);
     }
 
     /**
@@ -603,9 +625,10 @@ public final class SecurityClient {
      * @param request  the request with the roles to get
      * @param options  the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getRolesAsync(GetRolesRequest request, RequestOptions options, ActionListener<GetRolesResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::getRoles, options,
+    public Cancellable getRolesAsync(GetRolesRequest request, RequestOptions options, ActionListener<GetRolesResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::getRoles, options,
             GetRolesResponse::fromXContent, listener, emptySet());
     }
 
@@ -632,9 +655,10 @@ public final class SecurityClient {
      * @param request  the request containing the role to create or update
      * @param options  the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putRoleAsync(PutRoleRequest request, RequestOptions options, ActionListener<PutRoleResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::putRole, options,
+    public Cancellable putRoleAsync(PutRoleRequest request, RequestOptions options, ActionListener<PutRoleResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::putRole, options,
                 PutRoleResponse::fromXContent, listener, emptySet());
     }
 
@@ -660,10 +684,12 @@ public final class SecurityClient {
      * @param request the request with the role mapping name to be deleted.
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteRoleMappingAsync(DeleteRoleMappingRequest request, RequestOptions options,
-            ActionListener<DeleteRoleMappingResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::deleteRoleMapping, options,
+    public Cancellable deleteRoleMappingAsync(DeleteRoleMappingRequest request, RequestOptions options,
+                                              ActionListener<DeleteRoleMappingResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request,
+            SecurityRequestConverters::deleteRoleMapping, options,
                 DeleteRoleMappingResponse::fromXContent, listener, emptySet());
     }
 
@@ -688,9 +714,11 @@ public final class SecurityClient {
      * @param request the request with the role to delete
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteRoleAsync(DeleteRoleRequest request, RequestOptions options, ActionListener<DeleteRoleResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::deleteRole, options,
+    public Cancellable deleteRoleAsync(DeleteRoleRequest request, RequestOptions options,
+                                       ActionListener<DeleteRoleResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::deleteRole, options,
             DeleteRoleResponse::fromXContent, listener, singleton(404));
     }
 
@@ -717,9 +745,11 @@ public final class SecurityClient {
      * @param request the request for the token
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void createTokenAsync(CreateTokenRequest request, RequestOptions options, ActionListener<CreateTokenResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::createToken, options,
+    public Cancellable createTokenAsync(CreateTokenRequest request, RequestOptions options,
+                                        ActionListener<CreateTokenResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::createToken, options,
             CreateTokenResponse::fromXContent, listener, emptySet());
     }
 
@@ -742,14 +772,14 @@ public final class SecurityClient {
      * Asynchronously invalidates an OAuth2 token.
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-invalidate-token.html">
      * the docs</a> for more.
-     *
      * @param request the request to invalidate the token
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void invalidateTokenAsync(InvalidateTokenRequest request, RequestOptions options,
-                                     ActionListener<InvalidateTokenResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::invalidateToken, options,
+    public Cancellable invalidateTokenAsync(InvalidateTokenRequest request, RequestOptions options,
+                                            ActionListener<InvalidateTokenResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::invalidateToken, options,
             InvalidateTokenResponse::fromXContent, listener, emptySet());
     }
 
@@ -775,10 +805,13 @@ public final class SecurityClient {
      *
      * @param options  the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getBuiltinPrivilegesAsync(final RequestOptions options, final ActionListener<GetBuiltinPrivilegesResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(GetBuiltinPrivilegesRequest.INSTANCE,
-            GetBuiltinPrivilegesRequest::getRequest, options, GetBuiltinPrivilegesResponse::fromXContent, listener, emptySet());
+    public Cancellable getBuiltinPrivilegesAsync(final RequestOptions options,
+                                                 final ActionListener<GetBuiltinPrivilegesResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(GetBuiltinPrivilegesRequest.INSTANCE,
+            GetBuiltinPrivilegesRequest::getRequest, options, GetBuiltinPrivilegesResponse::fromXContent,
+            listener, emptySet());
     }
 
     /**
@@ -804,16 +837,16 @@ public final class SecurityClient {
      * Asynchronously get application privilege(s).
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-get-privileges.html">
      * the docs</a> for more.
-     *
-     * @param request  {@link GetPrivilegesRequest} with the application name and the privilege name.
+     *  @param request  {@link GetPrivilegesRequest} with the application name and the privilege name.
      *                 If no application name is provided, information about all privileges for all applications is retrieved.
      *                 If no privilege name is provided, information about all privileges of the specified application is retrieved.
      * @param options  the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getPrivilegesAsync(final GetPrivilegesRequest request, final RequestOptions options,
-                                   final ActionListener<GetPrivilegesResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::getPrivileges,
+    public Cancellable getPrivilegesAsync(final GetPrivilegesRequest request, final RequestOptions options,
+                                          final ActionListener<GetPrivilegesResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::getPrivileges,
             options, GetPrivilegesResponse::fromXContent, listener, emptySet());
     }
 
@@ -842,10 +875,11 @@ public final class SecurityClient {
      * @param options the request options (e.g. headers), use
      * {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putPrivilegesAsync(final PutPrivilegesRequest request, final RequestOptions options,
-            final ActionListener<PutPrivilegesResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::putPrivileges, options,
+    public Cancellable putPrivilegesAsync(final PutPrivilegesRequest request, final RequestOptions options,
+                                          final ActionListener<PutPrivilegesResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::putPrivileges, options,
                 PutPrivilegesResponse::fromXContent, listener, emptySet());
     }
 
@@ -872,10 +906,11 @@ public final class SecurityClient {
      * @param request  the request with the application privilege to delete
      * @param options  the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deletePrivilegesAsync(DeletePrivilegesRequest request, RequestOptions options,
-                                      ActionListener<DeletePrivilegesResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::deletePrivileges, options,
+    public Cancellable deletePrivilegesAsync(DeletePrivilegesRequest request, RequestOptions options,
+                                             ActionListener<DeletePrivilegesResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::deletePrivileges, options,
             DeletePrivilegesResponse::fromXContent, listener, singleton(404));
     }
 
@@ -902,10 +937,11 @@ public final class SecurityClient {
      * @param request the request to create a API key
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void createApiKeyAsync(final CreateApiKeyRequest request, final RequestOptions options,
-            final ActionListener<CreateApiKeyResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::createApiKey, options,
+    public Cancellable createApiKeyAsync(final CreateApiKeyRequest request, final RequestOptions options,
+                                         final ActionListener<CreateApiKeyResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::createApiKey, options,
                 CreateApiKeyResponse::fromXContent, listener, emptySet());
     }
 
@@ -932,10 +968,11 @@ public final class SecurityClient {
      * @param request the request to retrieve API key(s)
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getApiKeyAsync(final GetApiKeyRequest request, final RequestOptions options,
-            final ActionListener<GetApiKeyResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::getApiKey, options,
+    public Cancellable getApiKeyAsync(final GetApiKeyRequest request, final RequestOptions options,
+                                      final ActionListener<GetApiKeyResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::getApiKey, options,
                 GetApiKeyResponse::fromXContent, listener, emptySet());
     }
 
@@ -963,10 +1000,46 @@ public final class SecurityClient {
      * @param request the request to invalidate API key(s)
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void invalidateApiKeyAsync(final InvalidateApiKeyRequest request, final RequestOptions options,
-                                      final ActionListener<InvalidateApiKeyResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::invalidateApiKey, options,
+    public Cancellable invalidateApiKeyAsync(final InvalidateApiKeyRequest request, final RequestOptions options,
+                                             final ActionListener<InvalidateApiKeyResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::invalidateApiKey, options,
                 InvalidateApiKeyResponse::fromXContent, listener, emptySet());
     }
+
+    /**
+     * Get an Elasticsearch access token from an {@code X509Certificate} chain. The certificate chain is that of the client from a mutually
+     * authenticated TLS session, and it is validated by the PKI realms with {@code delegation.enabled} toggled to {@code true}.<br>
+     * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-delegate-pki-authentication.html"> the
+     * docs</a> for more details.
+     * 
+     * @param request the request containing the certificate chain
+     * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
+     * @return the response from the delegate-pki-authentication API key call
+     * @throws IOException in case there is a problem sending the request or parsing back the response
+     */
+    public DelegatePkiAuthenticationResponse delegatePkiAuthentication(DelegatePkiAuthenticationRequest request, RequestOptions options)
+            throws IOException {
+        return restHighLevelClient.performRequestAndParseEntity(request, SecurityRequestConverters::delegatePkiAuthentication, options,
+                DelegatePkiAuthenticationResponse::fromXContent, emptySet());
+    }
+
+    /**
+     * Asynchronously get an Elasticsearch access token from an {@code X509Certificate} chain. The certificate chain is that of the client
+     * from a mutually authenticated TLS session, and it is validated by the PKI realms with {@code delegation.enabled} toggled to
+     * {@code true}.<br>
+     * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/security-api-delegate-pki-authentication.html"> the
+     * docs</a> for more details.
+     * 
+     * @param request the request containing the certificate chain
+     * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
+     * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
+     */
+    public Cancellable delegatePkiAuthenticationAsync(DelegatePkiAuthenticationRequest request, RequestOptions options,
+            ActionListener<DelegatePkiAuthenticationResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, SecurityRequestConverters::delegatePkiAuthentication, options,
+                DelegatePkiAuthenticationResponse::fromXContent, listener, emptySet());
+    }
 }

+ 8 - 1
client/rest-high-level/src/main/java/org/elasticsearch/client/SecurityRequestConverters.java

@@ -28,6 +28,7 @@ import org.elasticsearch.client.security.ClearRealmCacheRequest;
 import org.elasticsearch.client.security.ClearRolesCacheRequest;
 import org.elasticsearch.client.security.CreateApiKeyRequest;
 import org.elasticsearch.client.security.CreateTokenRequest;
+import org.elasticsearch.client.security.DelegatePkiAuthenticationRequest;
 import org.elasticsearch.client.security.DeletePrivilegesRequest;
 import org.elasticsearch.client.security.DeleteRoleMappingRequest;
 import org.elasticsearch.client.security.DeleteRoleRequest;
@@ -221,6 +222,12 @@ final class SecurityRequestConverters {
         return request;
     }
 
+    static Request delegatePkiAuthentication(DelegatePkiAuthenticationRequest delegatePkiAuthenticationRequest) throws IOException {
+        Request request = new Request(HttpPost.METHOD_NAME, "/_security/delegate_pki");
+        request.setEntity(createEntity(delegatePkiAuthenticationRequest, REQUEST_BODY_CONTENT_TYPE));
+        return request;
+    }
+
     static Request invalidateToken(InvalidateTokenRequest invalidateTokenRequest) throws IOException {
         Request request = new Request(HttpDelete.METHOD_NAME, "/_security/oauth2/token");
         request.setEntity(createEntity(invalidateTokenRequest, REQUEST_BODY_CONTENT_TYPE));
@@ -294,7 +301,7 @@ final class SecurityRequestConverters {
         if (Strings.hasText(getApiKeyRequest.getRealmName())) {
             request.addParameter("realm_name", getApiKeyRequest.getRealmName());
         }
-
+        request.addParameter("owner", Boolean.toString(getApiKeyRequest.ownedByAuthenticatedUser()));
         return request;
     }
 

+ 51 - 31
client/rest-high-level/src/main/java/org/elasticsearch/client/SnapshotClient.java

@@ -79,10 +79,12 @@ public final class SnapshotClient {
      * @param getRepositoriesRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getRepositoryAsync(GetRepositoriesRequest getRepositoriesRequest, RequestOptions options,
-                                   ActionListener<GetRepositoriesResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(getRepositoriesRequest, SnapshotRequestConverters::getRepositories, options,
+    public Cancellable getRepositoryAsync(GetRepositoriesRequest getRepositoriesRequest, RequestOptions options,
+                                          ActionListener<GetRepositoriesResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(getRepositoriesRequest,
+            SnapshotRequestConverters::getRepositories, options,
             GetRepositoriesResponse::fromXContent, listener, emptySet());
     }
 
@@ -107,10 +109,12 @@ public final class SnapshotClient {
      * @param putRepositoryRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void createRepositoryAsync(PutRepositoryRequest putRepositoryRequest, RequestOptions options,
-                                      ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(putRepositoryRequest, SnapshotRequestConverters::createRepository, options,
+    public Cancellable createRepositoryAsync(PutRepositoryRequest putRepositoryRequest, RequestOptions options,
+                                             ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(putRepositoryRequest,
+            SnapshotRequestConverters::createRepository, options,
             AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -136,10 +140,12 @@ public final class SnapshotClient {
      * @param deleteRepositoryRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteRepositoryAsync(DeleteRepositoryRequest deleteRepositoryRequest, RequestOptions options,
-                                      ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(deleteRepositoryRequest, SnapshotRequestConverters::deleteRepository, options,
+    public Cancellable deleteRepositoryAsync(DeleteRepositoryRequest deleteRepositoryRequest, RequestOptions options,
+                                             ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(deleteRepositoryRequest,
+            SnapshotRequestConverters::deleteRepository, options,
             AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -165,10 +171,12 @@ public final class SnapshotClient {
      * @param verifyRepositoryRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void verifyRepositoryAsync(VerifyRepositoryRequest verifyRepositoryRequest, RequestOptions options,
-                                      ActionListener<VerifyRepositoryResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(verifyRepositoryRequest, SnapshotRequestConverters::verifyRepository, options,
+    public Cancellable verifyRepositoryAsync(VerifyRepositoryRequest verifyRepositoryRequest, RequestOptions options,
+                                             ActionListener<VerifyRepositoryResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(verifyRepositoryRequest,
+            SnapshotRequestConverters::verifyRepository, options,
             VerifyRepositoryResponse::fromXContent, listener, emptySet());
     }
 
@@ -194,10 +202,11 @@ public final class SnapshotClient {
      * @param cleanupRepositoryRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void cleanupRepositoryAsync(CleanupRepositoryRequest cleanupRepositoryRequest, RequestOptions options,
+    public Cancellable cleanupRepositoryAsync(CleanupRepositoryRequest cleanupRepositoryRequest, RequestOptions options,
                                        ActionListener<CleanupRepositoryResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(cleanupRepositoryRequest, SnapshotRequestConverters::cleanupRepository,
+        return restHighLevelClient.performRequestAsyncAndParseEntity(cleanupRepositoryRequest, SnapshotRequestConverters::cleanupRepository,
             options, CleanupRepositoryResponse::fromXContent, listener, emptySet());
     }
 
@@ -218,10 +227,12 @@ public final class SnapshotClient {
      * <p>
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore
      * API on elastic.co</a>
+     * @return cancellable that may be used to cancel the request
      */
-    public void createAsync(CreateSnapshotRequest createSnapshotRequest, RequestOptions options,
-                                    ActionListener<CreateSnapshotResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(createSnapshotRequest, SnapshotRequestConverters::createSnapshot, options,
+    public Cancellable createAsync(CreateSnapshotRequest createSnapshotRequest, RequestOptions options,
+                                   ActionListener<CreateSnapshotResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(createSnapshotRequest,
+            SnapshotRequestConverters::createSnapshot, options,
             CreateSnapshotResponse::fromXContent, listener, emptySet());
     }
 
@@ -244,13 +255,15 @@ public final class SnapshotClient {
      * Asynchronously get snapshots.
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-snapshots.html"> Snapshot and Restore
      * API on elastic.co</a>
-     *
-     * @param getSnapshotsRequest the request
+     *  @param getSnapshotsRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getAsync(GetSnapshotsRequest getSnapshotsRequest, RequestOptions options, ActionListener<GetSnapshotsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(getSnapshotsRequest, SnapshotRequestConverters::getSnapshots, options,
+    public Cancellable getAsync(GetSnapshotsRequest getSnapshotsRequest, RequestOptions options,
+                                ActionListener<GetSnapshotsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(getSnapshotsRequest,
+            SnapshotRequestConverters::getSnapshots, options,
             GetSnapshotsResponse::fromXContent, listener, emptySet());
     }
 
@@ -276,10 +289,12 @@ public final class SnapshotClient {
      * @param snapshotsStatusRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void statusAsync(SnapshotsStatusRequest snapshotsStatusRequest, RequestOptions options,
-                            ActionListener<SnapshotsStatusResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(snapshotsStatusRequest, SnapshotRequestConverters::snapshotsStatus, options,
+    public Cancellable statusAsync(SnapshotsStatusRequest snapshotsStatusRequest, RequestOptions options,
+                                   ActionListener<SnapshotsStatusResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(snapshotsStatusRequest,
+            SnapshotRequestConverters::snapshotsStatus, options,
             SnapshotsStatusResponse::fromXContent, listener, emptySet());
     }
 
@@ -306,10 +321,12 @@ public final class SnapshotClient {
      * @param restoreSnapshotRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void restoreAsync(RestoreSnapshotRequest restoreSnapshotRequest, RequestOptions options,
-                            ActionListener<RestoreSnapshotResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(restoreSnapshotRequest, SnapshotRequestConverters::restoreSnapshot, options,
+    public Cancellable restoreAsync(RestoreSnapshotRequest restoreSnapshotRequest, RequestOptions options,
+                                    ActionListener<RestoreSnapshotResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(restoreSnapshotRequest,
+            SnapshotRequestConverters::restoreSnapshot, options,
             RestoreSnapshotResponse::fromXContent, listener, emptySet());
     }
 
@@ -324,7 +341,8 @@ public final class SnapshotClient {
      * @throws IOException in case there is a problem sending the request or parsing back the response
      */
     public AcknowledgedResponse delete(DeleteSnapshotRequest deleteSnapshotRequest, RequestOptions options) throws IOException {
-        return restHighLevelClient.performRequestAndParseEntity(deleteSnapshotRequest, SnapshotRequestConverters::deleteSnapshot, options,
+        return restHighLevelClient.performRequestAndParseEntity(deleteSnapshotRequest,
+            SnapshotRequestConverters::deleteSnapshot, options,
             AcknowledgedResponse::fromXContent, emptySet());
     }
 
@@ -336,10 +354,12 @@ public final class SnapshotClient {
      * @param deleteSnapshotRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteAsync(DeleteSnapshotRequest deleteSnapshotRequest, RequestOptions options,
-                            ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(deleteSnapshotRequest, SnapshotRequestConverters::deleteSnapshot, options,
+    public Cancellable deleteAsync(DeleteSnapshotRequest deleteSnapshotRequest, RequestOptions options,
+                                   ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(deleteSnapshotRequest,
+            SnapshotRequestConverters::deleteSnapshot, options,
             AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 }

+ 17 - 12
client/rest-high-level/src/main/java/org/elasticsearch/client/TasksClient.java

@@ -65,12 +65,13 @@ public final class TasksClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void listAsync(ListTasksRequest request, RequestOptions options, ActionListener<ListTasksResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, TasksRequestConverters::listTasks, options,
+    public Cancellable listAsync(ListTasksRequest request, RequestOptions options, ActionListener<ListTasksResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, TasksRequestConverters::listTasks, options,
                 ListTasksResponse::fromXContent, listener, emptySet());
     }
-    
+
     /**
      * Get a task using the Task Management API.
      * See
@@ -82,9 +83,9 @@ public final class TasksClient {
      */
     public Optional<GetTaskResponse> get(GetTaskRequest request, RequestOptions options) throws IOException {
         return restHighLevelClient.performRequestAndParseOptionalEntity(request, TasksRequestConverters::getTask, options,
-                GetTaskResponse::fromXContent);        
-    }   
-    
+                GetTaskResponse::fromXContent);
+    }
+
     /**
      * Get a task using the Task Management API.
      * See
@@ -92,12 +93,14 @@ public final class TasksClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener an actionlistener that takes an optional response (404s are returned as an empty Optional)
+     * @return cancellable that may be used to cancel the request
      */
-    public void getAsync(GetTaskRequest request, RequestOptions options, ActionListener<Optional<GetTaskResponse>> listener) {
-        
-        restHighLevelClient.performRequestAsyncAndParseOptionalEntity(request, TasksRequestConverters::getTask, options,
+    public Cancellable getAsync(GetTaskRequest request, RequestOptions options,
+                                ActionListener<Optional<GetTaskResponse>> listener) {
+
+        return restHighLevelClient.performRequestAsyncAndParseOptionalEntity(request, TasksRequestConverters::getTask, options,
                 GetTaskResponse::fromXContent, listener);
-    }    
+    }
 
     /**
      * Cancel one or more cluster tasks using the Task Management API.
@@ -128,9 +131,11 @@ public final class TasksClient {
      * @param cancelTasksRequest the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void cancelAsync(CancelTasksRequest cancelTasksRequest, RequestOptions options, ActionListener<CancelTasksResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(
+    public Cancellable cancelAsync(CancelTasksRequest cancelTasksRequest, RequestOptions options,
+                                   ActionListener<CancelTasksResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
             cancelTasksRequest,
             TasksRequestConverters::cancelTasks,
             options,

+ 38 - 25
client/rest-high-level/src/main/java/org/elasticsearch/client/WatcherClient.java

@@ -71,10 +71,11 @@ public final class WatcherClient {
      * See <a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/watcher-api-start.html">
      * the docs</a> for more.
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
+     * @return cancellable that may be used to cancel the request
      */
-    public void startWatchServiceAsync(StartWatchServiceRequest request, RequestOptions options,
-            ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(
+    public Cancellable startWatchServiceAsync(StartWatchServiceRequest request, RequestOptions options,
+                                              ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
                 request, WatcherRequestConverters::startWatchService, options, AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -98,10 +99,11 @@ public final class WatcherClient {
      * the docs</a> for more.
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
+     * @return cancellable that may be used to cancel the request
      */
-    public void stopWatchServiceAsync(StopWatchServiceRequest request, RequestOptions options,
-            ActionListener<AcknowledgedResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(
+    public Cancellable stopWatchServiceAsync(StopWatchServiceRequest request, RequestOptions options,
+                                             ActionListener<AcknowledgedResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(
                 request, WatcherRequestConverters::stopWatchService, options, AcknowledgedResponse::fromXContent, listener, emptySet());
     }
 
@@ -126,10 +128,11 @@ public final class WatcherClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void putWatchAsync(PutWatchRequest request, RequestOptions options,
-                              ActionListener<PutWatchResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::putWatch, options,
+    public Cancellable putWatchAsync(PutWatchRequest request, RequestOptions options,
+                                     ActionListener<PutWatchResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::putWatch, options,
             PutWatchResponse::fromXContent, listener, emptySet());
     }
 
@@ -154,10 +157,11 @@ public final class WatcherClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void getWatchAsync(GetWatchRequest request, RequestOptions options,
-                              ActionListener<GetWatchResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::getWatch, options,
+    public Cancellable getWatchAsync(GetWatchRequest request, RequestOptions options,
+                                     ActionListener<GetWatchResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::getWatch, options,
             GetWatchResponse::fromXContent, listener, emptySet());
     }
 
@@ -183,10 +187,11 @@ public final class WatcherClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deactivateWatchAsync(DeactivateWatchRequest request, RequestOptions options,
-                                     ActionListener<DeactivateWatchResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::deactivateWatch, options,
+    public Cancellable deactivateWatchAsync(DeactivateWatchRequest request, RequestOptions options,
+                                            ActionListener<DeactivateWatchResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::deactivateWatch, options,
             DeactivateWatchResponse::fromXContent, listener, emptySet());
     }
 
@@ -211,9 +216,10 @@ public final class WatcherClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void deleteWatchAsync(DeleteWatchRequest request, RequestOptions options, ActionListener<DeleteWatchResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::deleteWatch, options,
+    public Cancellable deleteWatchAsync(DeleteWatchRequest request, RequestOptions options, ActionListener<DeleteWatchResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::deleteWatch, options,
             DeleteWatchResponse::fromXContent, listener, singleton(404));
     }
 
@@ -238,9 +244,10 @@ public final class WatcherClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon completion of the request
+     * @return cancellable that may be used to cancel the request
      */
-    public void ackWatchAsync(AckWatchRequest request, RequestOptions options, ActionListener<AckWatchResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::ackWatch, options,
+    public Cancellable ackWatchAsync(AckWatchRequest request, RequestOptions options, ActionListener<AckWatchResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::ackWatch, options,
             AckWatchResponse::fromXContent, listener, emptySet());
     }
 
@@ -265,9 +272,11 @@ public final class WatcherClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void activateWatchAsync(ActivateWatchRequest request, RequestOptions options, ActionListener<ActivateWatchResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::activateWatch, options,
+    public Cancellable activateWatchAsync(ActivateWatchRequest request, RequestOptions options,
+                                          ActionListener<ActivateWatchResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::activateWatch, options,
             ActivateWatchResponse::fromXContent, listener, singleton(404));
     }
 
@@ -292,9 +301,11 @@ public final class WatcherClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notifed upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void executeWatchAsync(ExecuteWatchRequest request, RequestOptions options, ActionListener<ExecuteWatchResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::executeWatch, options,
+    public Cancellable executeWatchAsync(ExecuteWatchRequest request, RequestOptions options,
+                                         ActionListener<ExecuteWatchResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::executeWatch, options,
             ExecuteWatchResponse::fromXContent, listener, emptySet());
     }
 
@@ -319,9 +330,11 @@ public final class WatcherClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void watcherStatsAsync(WatcherStatsRequest request, RequestOptions options, ActionListener<WatcherStatsResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::watcherStats, options,
+    public Cancellable watcherStatsAsync(WatcherStatsRequest request, RequestOptions options,
+                                         ActionListener<WatcherStatsResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, WatcherRequestConverters::watcherStats, options,
             WatcherStatsResponse::fromXContent, listener, emptySet());
     }
 

+ 7 - 5
client/rest-high-level/src/main/java/org/elasticsearch/client/XPackClient.java

@@ -67,10 +67,11 @@ public final class XPackClient {
      * @param request the request
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void infoAsync(XPackInfoRequest request, RequestOptions options,
-                                  ActionListener<XPackInfoResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, XPackRequestConverters::info, options,
+    public Cancellable infoAsync(XPackInfoRequest request, RequestOptions options,
+                                 ActionListener<XPackInfoResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, XPackRequestConverters::info, options,
             XPackInfoResponse::fromXContent, listener, emptySet());
     }
 
@@ -89,9 +90,10 @@ public final class XPackClient {
      * Asynchronously fetch usage information about X-Pack features from the cluster.
      * @param options the request options (e.g. headers), use {@link RequestOptions#DEFAULT} if nothing needs to be customized
      * @param listener the listener to be notified upon request completion
+     * @return cancellable that may be used to cancel the request
      */
-    public void usageAsync(XPackUsageRequest request, RequestOptions options, ActionListener<XPackUsageResponse> listener) {
-        restHighLevelClient.performRequestAsyncAndParseEntity(request, XPackRequestConverters::usage, options,
+    public Cancellable usageAsync(XPackUsageRequest request, RequestOptions options, ActionListener<XPackUsageResponse> listener) {
+        return restHighLevelClient.performRequestAsyncAndParseEntity(request, XPackRequestConverters::usage, options,
             XPackUsageResponse::fromXContent, listener, emptySet());
     }
 }

+ 13 - 11
client/rest-high-level/src/main/java/org/elasticsearch/client/ml/dataframe/DataFrameAnalyticsStats.java

@@ -28,6 +28,7 @@ import org.elasticsearch.common.xcontent.ObjectParser;
 import org.elasticsearch.common.xcontent.XContentParser;
 
 import java.io.IOException;
+import java.util.List;
 import java.util.Objects;
 
 import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
@@ -42,17 +43,18 @@ public class DataFrameAnalyticsStats {
     static final ParseField ID = new ParseField("id");
     static final ParseField STATE = new ParseField("state");
     static final ParseField FAILURE_REASON = new ParseField("failure_reason");
-    static final ParseField PROGRESS_PERCENT = new ParseField("progress_percent");
+    static final ParseField PROGRESS = new ParseField("progress");
     static final ParseField NODE = new ParseField("node");
     static final ParseField ASSIGNMENT_EXPLANATION = new ParseField("assignment_explanation");
 
+    @SuppressWarnings("unchecked")
     private static final ConstructingObjectParser<DataFrameAnalyticsStats, Void> PARSER =
         new ConstructingObjectParser<>("data_frame_analytics_stats", true,
             args -> new DataFrameAnalyticsStats(
                 (String) args[0],
                 (DataFrameAnalyticsState) args[1],
                 (String) args[2],
-                (Integer) args[3],
+                (List<PhaseProgress>) args[3],
                 (NodeAttributes) args[4],
                 (String) args[5]));
 
@@ -65,7 +67,7 @@ public class DataFrameAnalyticsStats {
             throw new IllegalArgumentException("Unsupported token [" + p.currentToken() + "]");
         }, STATE, ObjectParser.ValueType.STRING);
         PARSER.declareString(optionalConstructorArg(), FAILURE_REASON);
-        PARSER.declareInt(optionalConstructorArg(), PROGRESS_PERCENT);
+        PARSER.declareObjectArray(optionalConstructorArg(), PhaseProgress.PARSER, PROGRESS);
         PARSER.declareObject(optionalConstructorArg(), NodeAttributes.PARSER, NODE);
         PARSER.declareString(optionalConstructorArg(), ASSIGNMENT_EXPLANATION);
     }
@@ -73,17 +75,17 @@ public class DataFrameAnalyticsStats {
     private final String id;
     private final DataFrameAnalyticsState state;
     private final String failureReason;
-    private final Integer progressPercent;
+    private final List<PhaseProgress> progress;
     private final NodeAttributes node;
     private final String assignmentExplanation;
 
     public DataFrameAnalyticsStats(String id, DataFrameAnalyticsState state, @Nullable String failureReason,
-                                   @Nullable Integer progressPercent, @Nullable NodeAttributes node,
+                                   @Nullable List<PhaseProgress> progress, @Nullable NodeAttributes node,
                                    @Nullable String assignmentExplanation) {
         this.id = id;
         this.state = state;
         this.failureReason = failureReason;
-        this.progressPercent = progressPercent;
+        this.progress = progress;
         this.node = node;
         this.assignmentExplanation = assignmentExplanation;
     }
@@ -100,8 +102,8 @@ public class DataFrameAnalyticsStats {
         return failureReason;
     }
 
-    public Integer getProgressPercent() {
-        return progressPercent;
+    public List<PhaseProgress> getProgress() {
+        return progress;
     }
 
     public NodeAttributes getNode() {
@@ -121,14 +123,14 @@ public class DataFrameAnalyticsStats {
         return Objects.equals(id, other.id)
             && Objects.equals(state, other.state)
             && Objects.equals(failureReason, other.failureReason)
-            && Objects.equals(progressPercent, other.progressPercent)
+            && Objects.equals(progress, other.progress)
             && Objects.equals(node, other.node)
             && Objects.equals(assignmentExplanation, other.assignmentExplanation);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(id, state, failureReason, progressPercent, node, assignmentExplanation);
+        return Objects.hash(id, state, failureReason, progress, node, assignmentExplanation);
     }
 
     @Override
@@ -137,7 +139,7 @@ public class DataFrameAnalyticsStats {
             .add("id", id)
             .add("state", state)
             .add("failureReason", failureReason)
-            .add("progressPercent", progressPercent)
+            .add("progress", progress)
             .add("node", node)
             .add("assignmentExplanation", assignmentExplanation)
             .toString();

+ 5 - 1
client/rest-high-level/src/main/java/org/elasticsearch/client/ml/dataframe/MlDataFrameAnalysisNamedXContentProvider.java

@@ -32,6 +32,10 @@ public class MlDataFrameAnalysisNamedXContentProvider implements NamedXContentPr
             new NamedXContentRegistry.Entry(
                 DataFrameAnalysis.class,
                 OutlierDetection.NAME,
-                (p, c) -> OutlierDetection.fromXContent(p)));
+                (p, c) -> OutlierDetection.fromXContent(p)),
+            new NamedXContentRegistry.Entry(
+                DataFrameAnalysis.class,
+                Regression.NAME,
+                (p, c) -> Regression.fromXContent(p)));
     }
 }

+ 91 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/ml/dataframe/PhaseProgress.java

@@ -0,0 +1,91 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.elasticsearch.client.ml.dataframe;
+
+import org.elasticsearch.common.ParseField;
+import org.elasticsearch.common.inject.internal.ToStringBuilder;
+import org.elasticsearch.common.xcontent.ConstructingObjectParser;
+import org.elasticsearch.common.xcontent.ToXContentObject;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * A class that describes a phase and its progress as a percentage
+ */
+public class PhaseProgress implements ToXContentObject {
+
+    static final ParseField PHASE = new ParseField("phase");
+    static final ParseField PROGRESS_PERCENT = new ParseField("progress_percent");
+
+    public static final ConstructingObjectParser<PhaseProgress, Void> PARSER = new ConstructingObjectParser<>("phase_progress",
+        true, a -> new PhaseProgress((String) a[0], (int) a[1]));
+
+    static {
+        PARSER.declareString(ConstructingObjectParser.constructorArg(), PHASE);
+        PARSER.declareInt(ConstructingObjectParser.constructorArg(), PROGRESS_PERCENT);
+    }
+
+    private final String phase;
+    private final int progressPercent;
+
+    public PhaseProgress(String phase, int progressPercent) {
+        this.phase = Objects.requireNonNull(phase);
+        this.progressPercent = progressPercent;
+    }
+
+    public String getPhase() {
+        return phase;
+    }
+
+    public int getProgressPercent() {
+        return progressPercent;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(phase, progressPercent);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PhaseProgress that = (PhaseProgress) o;
+        return Objects.equals(phase, that.phase) && progressPercent == that.progressPercent;
+    }
+
+    @Override
+    public String toString() {
+        return new ToStringBuilder(getClass())
+            .add(PHASE.getPreferredName(), phase)
+            .add(PROGRESS_PERCENT.getPreferredName(), progressPercent)
+            .toString();
+    }
+
+    @Override
+    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
+        builder.startObject();
+        builder.field(PhaseProgress.PHASE.getPreferredName(), phase);
+        builder.field(PhaseProgress.PROGRESS_PERCENT.getPreferredName(), progressPercent);
+        builder.endObject();
+        return builder;
+    }
+}

+ 242 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/ml/dataframe/Regression.java

@@ -0,0 +1,242 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.elasticsearch.client.ml.dataframe;
+
+import org.elasticsearch.common.Nullable;
+import org.elasticsearch.common.ParseField;
+import org.elasticsearch.common.Strings;
+import org.elasticsearch.common.xcontent.ConstructingObjectParser;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.common.xcontent.XContentParser;
+
+import java.io.IOException;
+import java.util.Objects;
+
+public class Regression implements DataFrameAnalysis {
+
+    public static Regression fromXContent(XContentParser parser) {
+        return PARSER.apply(parser, null);
+    }
+
+    public static Builder builder(String dependentVariable) {
+        return new Builder(dependentVariable);
+    }
+
+    public static final ParseField NAME = new ParseField("regression");
+
+    static final ParseField DEPENDENT_VARIABLE = new ParseField("dependent_variable");
+    static final ParseField LAMBDA = new ParseField("lambda");
+    static final ParseField GAMMA = new ParseField("gamma");
+    static final ParseField ETA = new ParseField("eta");
+    static final ParseField MAXIMUM_NUMBER_TREES = new ParseField("maximum_number_trees");
+    static final ParseField FEATURE_BAG_FRACTION = new ParseField("feature_bag_fraction");
+    static final ParseField PREDICTION_FIELD_NAME = new ParseField("prediction_field_name");
+    static final ParseField TRAINING_PERCENT = new ParseField("training_percent");
+
+    private static final ConstructingObjectParser<Regression, Void> PARSER = new ConstructingObjectParser<>(NAME.getPreferredName(), true,
+        a -> new Regression(
+            (String) a[0],
+            (Double) a[1],
+            (Double) a[2],
+            (Double) a[3],
+            (Integer) a[4],
+            (Double) a[5],
+            (String) a[6],
+            (Double) a[7]));
+
+    static {
+        PARSER.declareString(ConstructingObjectParser.constructorArg(), DEPENDENT_VARIABLE);
+        PARSER.declareDouble(ConstructingObjectParser.optionalConstructorArg(), LAMBDA);
+        PARSER.declareDouble(ConstructingObjectParser.optionalConstructorArg(), GAMMA);
+        PARSER.declareDouble(ConstructingObjectParser.optionalConstructorArg(), ETA);
+        PARSER.declareInt(ConstructingObjectParser.optionalConstructorArg(), MAXIMUM_NUMBER_TREES);
+        PARSER.declareDouble(ConstructingObjectParser.optionalConstructorArg(), FEATURE_BAG_FRACTION);
+        PARSER.declareString(ConstructingObjectParser.optionalConstructorArg(), PREDICTION_FIELD_NAME);
+        PARSER.declareDouble(ConstructingObjectParser.optionalConstructorArg(), TRAINING_PERCENT);
+    }
+
+    private final String dependentVariable;
+    private final Double lambda;
+    private final Double gamma;
+    private final Double eta;
+    private final Integer maximumNumberTrees;
+    private final Double featureBagFraction;
+    private final String predictionFieldName;
+    private final Double trainingPercent;
+
+    private Regression(String dependentVariable,  @Nullable Double lambda, @Nullable Double gamma, @Nullable Double eta,
+                       @Nullable Integer maximumNumberTrees, @Nullable Double featureBagFraction, @Nullable String predictionFieldName,
+                       @Nullable Double trainingPercent) {
+        this.dependentVariable = Objects.requireNonNull(dependentVariable);
+        this.lambda = lambda;
+        this.gamma = gamma;
+        this.eta = eta;
+        this.maximumNumberTrees = maximumNumberTrees;
+        this.featureBagFraction = featureBagFraction;
+        this.predictionFieldName = predictionFieldName;
+        this.trainingPercent = trainingPercent;
+    }
+
+    @Override
+    public String getName() {
+        return NAME.getPreferredName();
+    }
+
+    public String getDependentVariable() {
+        return dependentVariable;
+    }
+
+    public Double getLambda() {
+        return lambda;
+    }
+
+    public Double getGamma() {
+        return gamma;
+    }
+
+    public Double getEta() {
+        return eta;
+    }
+
+    public Integer getMaximumNumberTrees() {
+        return maximumNumberTrees;
+    }
+
+    public Double getFeatureBagFraction() {
+        return featureBagFraction;
+    }
+
+    public String getPredictionFieldName() {
+        return predictionFieldName;
+    }
+
+    public Double getTrainingPercent() {
+        return trainingPercent;
+    }
+
+    @Override
+    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
+        builder.startObject();
+        builder.field(DEPENDENT_VARIABLE.getPreferredName(), dependentVariable);
+        if (lambda != null) {
+            builder.field(LAMBDA.getPreferredName(), lambda);
+        }
+        if (gamma != null) {
+            builder.field(GAMMA.getPreferredName(), gamma);
+        }
+        if (eta != null) {
+            builder.field(ETA.getPreferredName(), eta);
+        }
+        if (maximumNumberTrees != null) {
+            builder.field(MAXIMUM_NUMBER_TREES.getPreferredName(), maximumNumberTrees);
+        }
+        if (featureBagFraction != null) {
+            builder.field(FEATURE_BAG_FRACTION.getPreferredName(), featureBagFraction);
+        }
+        if (predictionFieldName != null) {
+            builder.field(PREDICTION_FIELD_NAME.getPreferredName(), predictionFieldName);
+        }
+        if (trainingPercent != null) {
+            builder.field(TRAINING_PERCENT.getPreferredName(), trainingPercent);
+        }
+        builder.endObject();
+        return builder;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(dependentVariable, lambda, gamma, eta, maximumNumberTrees, featureBagFraction, predictionFieldName,
+            trainingPercent);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        Regression that = (Regression) o;
+        return Objects.equals(dependentVariable, that.dependentVariable)
+            && Objects.equals(lambda, that.lambda)
+            && Objects.equals(gamma, that.gamma)
+            && Objects.equals(eta, that.eta)
+            && Objects.equals(maximumNumberTrees, that.maximumNumberTrees)
+            && Objects.equals(featureBagFraction, that.featureBagFraction)
+            && Objects.equals(predictionFieldName, that.predictionFieldName)
+            && Objects.equals(trainingPercent, that.trainingPercent);
+    }
+
+    @Override
+    public String toString() {
+        return Strings.toString(this);
+    }
+
+    public static class Builder {
+        private String dependentVariable;
+        private Double lambda;
+        private Double gamma;
+        private Double eta;
+        private Integer maximumNumberTrees;
+        private Double featureBagFraction;
+        private String predictionFieldName;
+        private Double trainingPercent;
+
+        private Builder(String dependentVariable) {
+            this.dependentVariable = Objects.requireNonNull(dependentVariable);
+        }
+
+        public Builder setLambda(Double lambda) {
+            this.lambda = lambda;
+            return this;
+        }
+
+        public Builder setGamma(Double gamma) {
+            this.gamma = gamma;
+            return this;
+        }
+
+        public Builder setEta(Double eta) {
+            this.eta = eta;
+            return this;
+        }
+
+        public Builder setMaximumNumberTrees(Integer maximumNumberTrees) {
+            this.maximumNumberTrees = maximumNumberTrees;
+            return this;
+        }
+
+        public Builder setFeatureBagFraction(Double featureBagFraction) {
+            this.featureBagFraction = featureBagFraction;
+            return this;
+        }
+
+        public Builder setPredictionFieldName(String predictionFieldName) {
+            this.predictionFieldName = predictionFieldName;
+            return this;
+        }
+
+        public Builder setTrainingPercent(Double trainingPercent) {
+            this.trainingPercent = trainingPercent;
+            return this;
+        }
+
+        public Regression build() {
+            return new Regression(dependentVariable, lambda, gamma, eta, maximumNumberTrees, featureBagFraction, predictionFieldName,
+                trainingPercent);
+        }
+    }
+}

+ 107 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/security/DelegatePkiAuthenticationRequest.java

@@ -0,0 +1,107 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.client.security;
+
+import org.elasticsearch.client.Validatable;
+import org.elasticsearch.client.ValidationException;
+import org.elasticsearch.common.xcontent.ToXContentObject;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+
+import java.io.IOException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Base64;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+
+import static java.util.Collections.unmodifiableList;
+
+public final class DelegatePkiAuthenticationRequest implements Validatable, ToXContentObject {
+
+    private final List<X509Certificate> x509CertificateChain;
+
+    public DelegatePkiAuthenticationRequest(final List<X509Certificate> x509CertificateChain) {
+        if (x509CertificateChain == null || x509CertificateChain.isEmpty()) {
+            throw new IllegalArgumentException("certificate chain must not be empty or null");
+        }
+        this.x509CertificateChain = unmodifiableList(x509CertificateChain);
+    }
+
+    @Override
+    public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
+        builder.startObject().startArray("x509_certificate_chain");
+        try {
+            for (X509Certificate cert : x509CertificateChain) {
+                 builder.value(Base64.getEncoder().encodeToString(cert.getEncoded()));
+             }
+         } catch (CertificateEncodingException e) {
+             throw new IOException(e);
+         }
+         return builder.endArray().endObject();
+    }
+
+    public List<X509Certificate> getCertificateChain() {
+        return this.x509CertificateChain;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        final DelegatePkiAuthenticationRequest that = (DelegatePkiAuthenticationRequest) o;
+        return Objects.equals(x509CertificateChain, that.x509CertificateChain);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(x509CertificateChain);
+    }
+
+    @Override
+    public Optional<ValidationException> validate() {
+        ValidationException validationException = new ValidationException();
+        if (false == isOrderedCertificateChain(x509CertificateChain)) {
+            validationException.addValidationError("certificates chain must be an ordered chain");
+        }
+        return validationException.validationErrors().isEmpty() ? Optional.empty() : Optional.of(validationException);
+    }
+
+    /**
+     * Checks that the {@code X509Certificate} list is ordered, such that the end-entity certificate is first and it is followed by any
+     * certificate authorities'. The check validates that the {@code issuer} of every certificate is the {@code subject} of the certificate
+     * in the next array position. No other certificate attributes are checked.
+     */
+    private static boolean isOrderedCertificateChain(List<X509Certificate> chain) {
+        for (int i = 1; i < chain.size(); i++) {
+            X509Certificate cert = chain.get(i - 1);
+            X509Certificate issuer = chain.get(i);
+            if (false == cert.getIssuerX500Principal().equals(issuer.getSubjectX500Principal())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+}

+ 88 - 0
client/rest-high-level/src/main/java/org/elasticsearch/client/security/DelegatePkiAuthenticationResponse.java

@@ -0,0 +1,88 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.client.security;
+
+import org.elasticsearch.common.ParseField;
+import org.elasticsearch.common.unit.TimeValue;
+import org.elasticsearch.common.xcontent.ConstructingObjectParser;
+import org.elasticsearch.common.xcontent.XContentParser;
+
+import java.io.IOException;
+import java.util.Objects;
+
+import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg;
+
+public final class DelegatePkiAuthenticationResponse {
+
+    private final String accessToken;
+    private final String type;
+    private final TimeValue expiresIn;
+
+    public DelegatePkiAuthenticationResponse(String accessToken, String type, TimeValue expiresIn) {
+        this.accessToken = accessToken;
+        this.type = type;
+        this.expiresIn = expiresIn;
+    }
+
+    public String getAccessToken() {
+        return accessToken;
+    }
+
+    public String getType() {
+        return type;
+    }
+
+    public TimeValue getExpiresIn() {
+        return expiresIn;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        final DelegatePkiAuthenticationResponse that = (DelegatePkiAuthenticationResponse) o;
+        return Objects.equals(accessToken, that.accessToken) &&
+            Objects.equals(type, that.type) &&
+            Objects.equals(expiresIn, that.expiresIn);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(accessToken, type, expiresIn);
+    }
+
+    private static final ConstructingObjectParser<DelegatePkiAuthenticationResponse, Void> PARSER = new ConstructingObjectParser<>(
+            "delegate_pki_response", true,
+            args -> new DelegatePkiAuthenticationResponse((String) args[0], (String) args[1], TimeValue.timeValueSeconds((Long) args[2])));
+
+    static {
+        PARSER.declareString(constructorArg(), new ParseField("access_token"));
+        PARSER.declareString(constructorArg(), new ParseField("type"));
+        PARSER.declareLong(constructorArg(), new ParseField("expires_in"));
+    }
+
+    public static DelegatePkiAuthenticationResponse fromXContent(XContentParser parser) throws IOException {
+        return PARSER.parse(parser, null);
+    }
+}

+ 32 - 10
client/rest-high-level/src/main/java/org/elasticsearch/client/security/GetApiKeyRequest.java

@@ -36,13 +36,14 @@ public final class GetApiKeyRequest implements Validatable, ToXContentObject {
     private final String userName;
     private final String id;
     private final String name;
+    private final boolean ownedByAuthenticatedUser;
 
     // pkg scope for testing
     GetApiKeyRequest(@Nullable String realmName, @Nullable String userName, @Nullable String apiKeyId,
-            @Nullable String apiKeyName) {
+                     @Nullable String apiKeyName, boolean ownedByAuthenticatedUser) {
         if (Strings.hasText(realmName) == false && Strings.hasText(userName) == false && Strings.hasText(apiKeyId) == false
-                && Strings.hasText(apiKeyName) == false) {
-            throwValidationError("One of [api key id, api key name, username, realm name] must be specified");
+                && Strings.hasText(apiKeyName) == false && ownedByAuthenticatedUser == false) {
+            throwValidationError("One of [api key id, api key name, username, realm name] must be specified if [owner] flag is false");
         }
         if (Strings.hasText(apiKeyId) || Strings.hasText(apiKeyName)) {
             if (Strings.hasText(realmName) || Strings.hasText(userName)) {
@@ -50,6 +51,11 @@ public final class GetApiKeyRequest implements Validatable, ToXContentObject {
                         "username or realm name must not be specified when the api key id or api key name is specified");
             }
         }
+        if (ownedByAuthenticatedUser) {
+            if (Strings.hasText(realmName) || Strings.hasText(userName)) {
+                throwValidationError("neither username nor realm-name may be specified when retrieving owned API keys");
+            }
+        }
         if (Strings.hasText(apiKeyId) && Strings.hasText(apiKeyName)) {
             throwValidationError("only one of [api key id, api key name] can be specified");
         }
@@ -57,6 +63,7 @@ public final class GetApiKeyRequest implements Validatable, ToXContentObject {
         this.userName = userName;
         this.id = apiKeyId;
         this.name = apiKeyName;
+        this.ownedByAuthenticatedUser = ownedByAuthenticatedUser;
     }
 
     private void throwValidationError(String message) {
@@ -79,13 +86,17 @@ public final class GetApiKeyRequest implements Validatable, ToXContentObject {
         return name;
     }
 
+    public boolean ownedByAuthenticatedUser() {
+        return ownedByAuthenticatedUser;
+    }
+
     /**
      * Creates get API key request for given realm name
      * @param realmName realm name
      * @return {@link GetApiKeyRequest}
      */
     public static GetApiKeyRequest usingRealmName(String realmName) {
-        return new GetApiKeyRequest(realmName, null, null, null);
+        return new GetApiKeyRequest(realmName, null, null, null, false);
     }
 
     /**
@@ -94,7 +105,7 @@ public final class GetApiKeyRequest implements Validatable, ToXContentObject {
      * @return {@link GetApiKeyRequest}
      */
     public static GetApiKeyRequest usingUserName(String userName) {
-        return new GetApiKeyRequest(null, userName, null, null);
+        return new GetApiKeyRequest(null, userName, null, null, false);
     }
 
     /**
@@ -104,25 +115,36 @@ public final class GetApiKeyRequest implements Validatable, ToXContentObject {
      * @return {@link GetApiKeyRequest}
      */
     public static GetApiKeyRequest usingRealmAndUserName(String realmName, String userName) {
-        return new GetApiKeyRequest(realmName, userName, null, null);
+        return new GetApiKeyRequest(realmName, userName, null, null, false);
     }
 
     /**
      * Creates get API key request for given api key id
      * @param apiKeyId api key id
+     * @param ownedByAuthenticatedUser set {@code true} if the request is only for the API keys owned by current
+     * authenticated user else{@code false}
      * @return {@link GetApiKeyRequest}
      */
-    public static GetApiKeyRequest usingApiKeyId(String apiKeyId) {
-        return new GetApiKeyRequest(null, null, apiKeyId, null);
+    public static GetApiKeyRequest usingApiKeyId(String apiKeyId, boolean ownedByAuthenticatedUser) {
+        return new GetApiKeyRequest(null, null, apiKeyId, null, ownedByAuthenticatedUser);
     }
 
     /**
      * Creates get API key request for given api key name
      * @param apiKeyName api key name
+     * @param ownedByAuthenticatedUser set {@code true} if the request is only for the API keys owned by current
+     * authenticated user else{@code false}
      * @return {@link GetApiKeyRequest}
      */
-    public static GetApiKeyRequest usingApiKeyName(String apiKeyName) {
-        return new GetApiKeyRequest(null, null, null, apiKeyName);
+    public static GetApiKeyRequest usingApiKeyName(String apiKeyName, boolean ownedByAuthenticatedUser) {
+        return new GetApiKeyRequest(null, null, null, apiKeyName, ownedByAuthenticatedUser);
+    }
+
+    /**
+     * Creates get api key request to retrieve api key information for the api keys owned by the current authenticated user.
+     */
+    public static GetApiKeyRequest forOwnedApiKeys() {
+        return new GetApiKeyRequest(null, null, null, null, true);
     }
 
     @Override

+ 33 - 10
client/rest-high-level/src/main/java/org/elasticsearch/client/security/InvalidateApiKeyRequest.java

@@ -36,13 +36,14 @@ public final class InvalidateApiKeyRequest implements Validatable, ToXContentObj
     private final String userName;
     private final String id;
     private final String name;
+    private final boolean ownedByAuthenticatedUser;
 
     // pkg scope for testing
     InvalidateApiKeyRequest(@Nullable String realmName, @Nullable String userName, @Nullable String apiKeyId,
-            @Nullable String apiKeyName) {
+                            @Nullable String apiKeyName, boolean ownedByAuthenticatedUser) {
         if (Strings.hasText(realmName) == false && Strings.hasText(userName) == false && Strings.hasText(apiKeyId) == false
-                && Strings.hasText(apiKeyName) == false) {
-            throwValidationError("One of [api key id, api key name, username, realm name] must be specified");
+                && Strings.hasText(apiKeyName) == false && ownedByAuthenticatedUser == false) {
+            throwValidationError("One of [api key id, api key name, username, realm name] must be specified if [owner] flag is false");
         }
         if (Strings.hasText(apiKeyId) || Strings.hasText(apiKeyName)) {
             if (Strings.hasText(realmName) || Strings.hasText(userName)) {
@@ -50,6 +51,11 @@ public final class InvalidateApiKeyRequest implements Validatable, ToXContentObj
                         "username or realm name must not be specified when the api key id or api key name is specified");
             }
         }
+        if (ownedByAuthenticatedUser) {
+            if (Strings.hasText(realmName) || Strings.hasText(userName)) {
+                throwValidationError("neither username nor realm-name may be specified when invalidating owned API keys");
+            }
+        }
         if (Strings.hasText(apiKeyId) && Strings.hasText(apiKeyName)) {
             throwValidationError("only one of [api key id, api key name] can be specified");
         }
@@ -57,6 +63,7 @@ public final class InvalidateApiKeyRequest implements Validatable, ToXContentObj
         this.userName = userName;
         this.id = apiKeyId;
         this.name = apiKeyName;
+        this.ownedByAuthenticatedUser = ownedByAuthenticatedUser;
     }
 
     private void throwValidationError(String message) {
@@ -79,13 +86,17 @@ public final class InvalidateApiKeyRequest implements Validatable, ToXContentObj
         return name;
     }
 
+    public boolean ownedByAuthenticatedUser() {
+        return ownedByAuthenticatedUser;
+    }
+
     /**
      * Creates invalidate API key request for given realm name
      * @param realmName realm name
      * @return {@link InvalidateApiKeyRequest}
      */
     public static InvalidateApiKeyRequest usingRealmName(String realmName) {
-        return new InvalidateApiKeyRequest(realmName, null, null, null);
+        return new InvalidateApiKeyRequest(realmName, null, null, null, false);
     }
 
     /**
@@ -94,7 +105,7 @@ public final class InvalidateApiKeyRequest implements Validatable, ToXContentObj
      * @return {@link InvalidateApiKeyRequest}
      */
     public static InvalidateApiKeyRequest usingUserName(String userName) {
-        return new InvalidateApiKeyRequest(null, userName, null, null);
+        return new InvalidateApiKeyRequest(null, userName, null, null, false);
     }
 
     /**
@@ -104,25 +115,36 @@ public final class InvalidateApiKeyRequest implements Validatable, ToXContentObj
      * @return {@link InvalidateApiKeyRequest}
      */
     public static InvalidateApiKeyRequest usingRealmAndUserName(String realmName, String userName) {
-        return new InvalidateApiKeyRequest(realmName, userName, null, null);
+        return new InvalidateApiKeyRequest(realmName, userName, null, null, false);
     }
 
     /**
      * Creates invalidate API key request for given api key id
      * @param apiKeyId api key id
+     * @param ownedByAuthenticatedUser set {@code true} if the request is only for the API keys owned by current authenticated user else
+     * {@code false}
      * @return {@link InvalidateApiKeyRequest}
      */
-    public static InvalidateApiKeyRequest usingApiKeyId(String apiKeyId) {
-        return new InvalidateApiKeyRequest(null, null, apiKeyId, null);
+    public static InvalidateApiKeyRequest usingApiKeyId(String apiKeyId, boolean ownedByAuthenticatedUser) {
+        return new InvalidateApiKeyRequest(null, null, apiKeyId, null, ownedByAuthenticatedUser);
     }
 
     /**
      * Creates invalidate API key request for given api key name
      * @param apiKeyName api key name
+     * @param ownedByAuthenticatedUser set {@code true} if the request is only for the API keys owned by current authenticated user else
+     * {@code false}
      * @return {@link InvalidateApiKeyRequest}
      */
-    public static InvalidateApiKeyRequest usingApiKeyName(String apiKeyName) {
-        return new InvalidateApiKeyRequest(null, null, null, apiKeyName);
+    public static InvalidateApiKeyRequest usingApiKeyName(String apiKeyName, boolean ownedByAuthenticatedUser) {
+        return new InvalidateApiKeyRequest(null, null, null, apiKeyName, ownedByAuthenticatedUser);
+    }
+
+    /**
+     * Creates invalidate api key request to invalidate api keys owned by the current authenticated user.
+     */
+    public static InvalidateApiKeyRequest forOwnedApiKeys() {
+        return new InvalidateApiKeyRequest(null, null, null, null, true);
     }
 
     @Override
@@ -140,6 +162,7 @@ public final class InvalidateApiKeyRequest implements Validatable, ToXContentObj
         if (name != null) {
             builder.field("name", name);
         }
+        builder.field("owner", ownedByAuthenticatedUser);
         return builder.endObject();
     }
 }

+ 45 - 3
client/rest-high-level/src/test/java/org/elasticsearch/client/MachineLearningIT.java

@@ -123,6 +123,7 @@ import org.elasticsearch.client.ml.dataframe.DataFrameAnalyticsSource;
 import org.elasticsearch.client.ml.dataframe.DataFrameAnalyticsState;
 import org.elasticsearch.client.ml.dataframe.DataFrameAnalyticsStats;
 import org.elasticsearch.client.ml.dataframe.OutlierDetection;
+import org.elasticsearch.client.ml.dataframe.PhaseProgress;
 import org.elasticsearch.client.ml.dataframe.QueryConfig;
 import org.elasticsearch.client.ml.dataframe.evaluation.regression.MeanSquaredErrorMetric;
 import org.elasticsearch.client.ml.dataframe.evaluation.regression.RSquaredMetric;
@@ -1214,9 +1215,9 @@ public class MachineLearningIT extends ESRestHighLevelClientTestCase {
         assertThat(remainingIds, not(hasItem(deletedEvent)));
     }
 
-    public void testPutDataFrameAnalyticsConfig() throws Exception {
+    public void testPutDataFrameAnalyticsConfig_GivenOutlierDetectionAnalysis() throws Exception {
         MachineLearningClient machineLearningClient = highLevelClient().machineLearning();
-        String configId = "put-test-config";
+        String configId = "test-put-df-analytics-outlier-detection";
         DataFrameAnalyticsConfig config = DataFrameAnalyticsConfig.builder()
             .setId(configId)
             .setSource(DataFrameAnalyticsSource.builder()
@@ -1246,6 +1247,41 @@ public class MachineLearningIT extends ESRestHighLevelClientTestCase {
         assertThat(createdConfig.getDescription(), equalTo("some description"));
     }
 
+    public void testPutDataFrameAnalyticsConfig_GivenRegression() throws Exception {
+        MachineLearningClient machineLearningClient = highLevelClient().machineLearning();
+        String configId = "test-put-df-analytics-regression";
+        DataFrameAnalyticsConfig config = DataFrameAnalyticsConfig.builder()
+            .setId(configId)
+            .setSource(DataFrameAnalyticsSource.builder()
+                .setIndex("put-test-source-index")
+                .build())
+            .setDest(DataFrameAnalyticsDest.builder()
+                .setIndex("put-test-dest-index")
+                .build())
+            .setAnalysis(org.elasticsearch.client.ml.dataframe.Regression
+                .builder("my_dependent_variable")
+                .setTrainingPercent(80.0)
+                .build())
+            .setDescription("this is a regression")
+            .build();
+
+        createIndex("put-test-source-index", defaultMappingForTest());
+
+        PutDataFrameAnalyticsResponse putDataFrameAnalyticsResponse = execute(
+            new PutDataFrameAnalyticsRequest(config),
+            machineLearningClient::putDataFrameAnalytics, machineLearningClient::putDataFrameAnalyticsAsync);
+        DataFrameAnalyticsConfig createdConfig = putDataFrameAnalyticsResponse.getConfig();
+        assertThat(createdConfig.getId(), equalTo(config.getId()));
+        assertThat(createdConfig.getSource().getIndex(), equalTo(config.getSource().getIndex()));
+        assertThat(createdConfig.getSource().getQueryConfig(), equalTo(new QueryConfig(new MatchAllQueryBuilder())));  // default value
+        assertThat(createdConfig.getDest().getIndex(), equalTo(config.getDest().getIndex()));
+        assertThat(createdConfig.getDest().getResultsField(), equalTo("ml"));  // default value
+        assertThat(createdConfig.getAnalysis(), equalTo(config.getAnalysis()));
+        assertThat(createdConfig.getAnalyzedFields(), equalTo(config.getAnalyzedFields()));
+        assertThat(createdConfig.getModelMemoryLimit(), equalTo(ByteSizeValue.parseBytesSizeValue("1gb", "")));  // default value
+        assertThat(createdConfig.getDescription(), equalTo("this is a regression"));
+    }
+
     public void testGetDataFrameAnalyticsConfig_SingleConfig() throws Exception {
         MachineLearningClient machineLearningClient = highLevelClient().machineLearning();
         String configId = "get-test-config";
@@ -1377,11 +1413,17 @@ public class MachineLearningIT extends ESRestHighLevelClientTestCase {
         assertThat(stats.getId(), equalTo(configId));
         assertThat(stats.getState(), equalTo(DataFrameAnalyticsState.STOPPED));
         assertNull(stats.getFailureReason());
-        assertNull(stats.getProgressPercent());
         assertNull(stats.getNode());
         assertNull(stats.getAssignmentExplanation());
         assertThat(statsResponse.getNodeFailures(), hasSize(0));
         assertThat(statsResponse.getTaskFailures(), hasSize(0));
+        List<PhaseProgress> progress = stats.getProgress();
+        assertThat(progress, is(notNullValue()));
+        assertThat(progress.size(), equalTo(4));
+        assertThat(progress.get(0), equalTo(new PhaseProgress("reindexing", 0)));
+        assertThat(progress.get(1), equalTo(new PhaseProgress("loading_data", 0)));
+        assertThat(progress.get(2), equalTo(new PhaseProgress("analyzing", 0)));
+        assertThat(progress.get(3), equalTo(new PhaseProgress("writing_results", 0)));
     }
 
     public void testStartDataFrameAnalyticsConfig() throws Exception {

+ 4 - 4
client/rest-high-level/src/test/java/org/elasticsearch/client/RestHighLevelClientTests.java

@@ -20,7 +20,6 @@
 package org.elasticsearch.client;
 
 import com.fasterxml.jackson.core.JsonParseException;
-
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpHost;
 import org.apache.http.HttpResponse;
@@ -677,7 +676,7 @@ public class RestHighLevelClientTests extends ESTestCase {
 
     public void testProvidedNamedXContents() {
         List<NamedXContentRegistry.Entry> namedXContents = RestHighLevelClient.getProvidedNamedXContents();
-        assertEquals(36, namedXContents.size());
+        assertEquals(37, namedXContents.size());
         Map<Class<?>, Integer> categories = new HashMap<>();
         List<String> names = new ArrayList<>();
         for (NamedXContentRegistry.Entry namedXContent : namedXContents) {
@@ -711,8 +710,9 @@ public class RestHighLevelClientTests extends ESTestCase {
         assertTrue(names.contains(ShrinkAction.NAME));
         assertTrue(names.contains(FreezeAction.NAME));
         assertTrue(names.contains(SetPriorityAction.NAME));
-        assertEquals(Integer.valueOf(1), categories.get(DataFrameAnalysis.class));
+        assertEquals(Integer.valueOf(2), categories.get(DataFrameAnalysis.class));
         assertTrue(names.contains(OutlierDetection.NAME.getPreferredName()));
+        assertTrue(names.contains(org.elasticsearch.client.ml.dataframe.Regression.NAME.getPreferredName()));
         assertEquals(Integer.valueOf(1), categories.get(SyncConfig.class));
         assertTrue(names.contains(TimeSyncConfig.NAME));
         assertEquals(Integer.valueOf(2), categories.get(org.elasticsearch.client.ml.dataframe.evaluation.Evaluation.class));
@@ -901,7 +901,7 @@ public class RestHighLevelClientTests extends ESTestCase {
     private static void assertAsyncMethod(Map<String, Set<Method>> methods, Method method, String apiName) {
         assertTrue("async method [" + method.getName() + "] doesn't have corresponding sync method",
                 methods.containsKey(apiName.substring(0, apiName.length() - 6)));
-        assertThat("async method [" + method + "] should return void", method.getReturnType(), equalTo(Void.TYPE));
+        assertThat("async method [" + method + "] should return Cancellable", method.getReturnType(), equalTo(Cancellable.class));
         assertEquals("async method [" + method + "] should not throw any exceptions", 0, method.getExceptionTypes().length);
         if (APIS_WITHOUT_REQUEST_OBJECT.contains(apiName.replaceAll("_async$", ""))) {
             assertEquals(2, method.getParameterTypes().length);

+ 21 - 4
client/rest-high-level/src/test/java/org/elasticsearch/client/SecurityRequestConvertersTests.java

@@ -26,6 +26,7 @@ import org.apache.http.client.methods.HttpPut;
 import org.elasticsearch.client.security.ChangePasswordRequest;
 import org.elasticsearch.client.security.CreateApiKeyRequest;
 import org.elasticsearch.client.security.CreateTokenRequest;
+import org.elasticsearch.client.security.DelegatePkiAuthenticationRequest;
 import org.elasticsearch.client.security.DeletePrivilegesRequest;
 import org.elasticsearch.client.security.DeleteRoleMappingRequest;
 import org.elasticsearch.client.security.DeleteRoleRequest;
@@ -58,6 +59,7 @@ import org.elasticsearch.common.unit.TimeValue;
 import org.elasticsearch.test.ESTestCase;
 
 import java.io.IOException;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -67,6 +69,8 @@ import java.util.Map;
 
 import static org.elasticsearch.client.RequestConvertersTests.assertToXContentBody;
 import static org.hamcrest.Matchers.equalTo;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 public class SecurityRequestConvertersTests extends ESTestCase {
 
@@ -304,6 +308,18 @@ public class SecurityRequestConvertersTests extends ESTestCase {
         assertToXContentBody(createTokenRequest, request.getEntity());
     }
 
+    public void testDelegatePkiAuthentication() throws Exception {
+        X509Certificate mockCertificate = mock(X509Certificate.class);
+        when(mockCertificate.getEncoded()).thenReturn(new byte[0]);
+        DelegatePkiAuthenticationRequest delegatePkiAuthenticationRequest = new DelegatePkiAuthenticationRequest(
+                Arrays.asList(mockCertificate));
+        Request request = SecurityRequestConverters.delegatePkiAuthentication(delegatePkiAuthenticationRequest);
+        assertEquals(HttpPost.METHOD_NAME, request.getMethod());
+        assertEquals("/_security/delegate_pki", request.getEndpoint());
+        assertEquals(0, request.getParameters().size());
+        assertToXContentBody(delegatePkiAuthenticationRequest, request.getEntity());
+    }
+
     public void testGetApplicationPrivilege() throws Exception {
         final String application = randomAlphaOfLength(6);
         final String privilege = randomAlphaOfLength(4);
@@ -446,10 +462,11 @@ public class SecurityRequestConvertersTests extends ESTestCase {
         final Request request = SecurityRequestConverters.getApiKey(getApiKeyRequest);
         assertEquals(HttpGet.METHOD_NAME, request.getMethod());
         assertEquals("/_security/api_key", request.getEndpoint());
-        Map<String, String> mapOfParameters = new HashMap<>();
-        mapOfParameters.put("realm_name", realmName);
-        mapOfParameters.put("username", userName);
-        assertThat(request.getParameters(), equalTo(mapOfParameters));
+        Map<String, String> expectedMapOfParameters = new HashMap<>();
+        expectedMapOfParameters.put("realm_name", realmName);
+        expectedMapOfParameters.put("username", userName);
+        expectedMapOfParameters.put("owner", Boolean.FALSE.toString());
+        assertThat(request.getParameters(), equalTo(expectedMapOfParameters));
     }
 
     public void testInvalidateApiKey() throws IOException {

+ 17 - 4
client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/MlClientDocumentationIT.java

@@ -139,6 +139,7 @@ import org.elasticsearch.client.ml.dataframe.DataFrameAnalyticsState;
 import org.elasticsearch.client.ml.dataframe.DataFrameAnalyticsStats;
 import org.elasticsearch.client.ml.dataframe.OutlierDetection;
 import org.elasticsearch.client.ml.dataframe.QueryConfig;
+import org.elasticsearch.client.ml.dataframe.Regression;
 import org.elasticsearch.client.ml.dataframe.evaluation.EvaluationMetric;
 import org.elasticsearch.client.ml.dataframe.evaluation.softclassification.AucRocMetric;
 import org.elasticsearch.client.ml.dataframe.evaluation.softclassification.BinarySoftClassification;
@@ -2923,16 +2924,28 @@ public class MlClientDocumentationIT extends ESRestHighLevelClientTestCase {
                 .build();
             // end::put-data-frame-analytics-dest-config
 
-            // tag::put-data-frame-analytics-analysis-default
+            // tag::put-data-frame-analytics-outlier-detection-default
             DataFrameAnalysis outlierDetection = OutlierDetection.createDefault(); // <1>
-            // end::put-data-frame-analytics-analysis-default
+            // end::put-data-frame-analytics-outlier-detection-default
 
-            // tag::put-data-frame-analytics-analysis-customized
+            // tag::put-data-frame-analytics-outlier-detection-customized
             DataFrameAnalysis outlierDetectionCustomized = OutlierDetection.builder() // <1>
                 .setMethod(OutlierDetection.Method.DISTANCE_KNN) // <2>
                 .setNNeighbors(5) // <3>
                 .build();
-            // end::put-data-frame-analytics-analysis-customized
+            // end::put-data-frame-analytics-outlier-detection-customized
+
+            // tag::put-data-frame-analytics-regression
+            DataFrameAnalysis regression = Regression.builder("my_dependent_variable") // <1>
+                .setLambda(1.0) // <2>
+                .setGamma(5.5) // <3>
+                .setEta(5.5) // <4>
+                .setMaximumNumberTrees(50) // <5>
+                .setFeatureBagFraction(0.4) // <6>
+                .setPredictionFieldName("my_prediction_field_name") // <7>
+                .setTrainingPercent(50.0) // <8>
+                .build();
+            // end::put-data-frame-analytics-regression
 
             // tag::put-data-frame-analytics-analyzed-fields
             FetchSourceContext analyzedFields =

+ 131 - 6
client/rest-high-level/src/test/java/org/elasticsearch/client/documentation/SecurityDocumentationIT.java

@@ -37,6 +37,8 @@ import org.elasticsearch.client.security.CreateApiKeyRequest;
 import org.elasticsearch.client.security.CreateApiKeyResponse;
 import org.elasticsearch.client.security.CreateTokenRequest;
 import org.elasticsearch.client.security.CreateTokenResponse;
+import org.elasticsearch.client.security.DelegatePkiAuthenticationRequest;
+import org.elasticsearch.client.security.DelegatePkiAuthenticationResponse;
 import org.elasticsearch.client.security.DeletePrivilegesRequest;
 import org.elasticsearch.client.security.DeletePrivilegesResponse;
 import org.elasticsearch.client.security.DeleteRoleMappingRequest;
@@ -77,6 +79,7 @@ import org.elasticsearch.client.security.PutUserRequest;
 import org.elasticsearch.client.security.PutUserResponse;
 import org.elasticsearch.client.security.RefreshPolicy;
 import org.elasticsearch.client.security.TemplateRoleName;
+import org.elasticsearch.client.security.AuthenticateResponse.RealmInfo;
 import org.elasticsearch.client.security.support.ApiKey;
 import org.elasticsearch.client.security.support.CertificateInfo;
 import org.elasticsearch.client.security.support.expressiondsl.RoleMapperExpression;
@@ -99,6 +102,11 @@ import org.hamcrest.Matchers;
 import javax.crypto.SecretKeyFactory;
 import javax.crypto.spec.PBEKeySpec;
 import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -1917,7 +1925,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
                 Instant.now().plusMillis(expiration.getMillis()), false, "test_user", "default_file");
         {
             // tag::get-api-key-id-request
-            GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.usingApiKeyId(createApiKeyResponse1.getId());
+            GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.usingApiKeyId(createApiKeyResponse1.getId(), false);
             // end::get-api-key-id-request
 
             // tag::get-api-key-execute
@@ -1931,7 +1939,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
 
         {
             // tag::get-api-key-name-request
-            GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.usingApiKeyName(createApiKeyResponse1.getName());
+            GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.usingApiKeyName(createApiKeyResponse1.getName(), false);
             // end::get-api-key-name-request
 
             GetApiKeyResponse getApiKeyResponse = client.security().getApiKey(getApiKeyRequest, RequestOptions.DEFAULT);
@@ -1965,6 +1973,18 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             verifyApiKey(getApiKeyResponse.getApiKeyInfos().get(0), expectedApiKeyInfo);
         }
 
+        {
+            // tag::get-api-keys-owned-by-authenticated-user-request
+            GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.forOwnedApiKeys();
+            // end::get-api-keys-owned-by-authenticated-user-request
+
+            GetApiKeyResponse getApiKeyResponse = client.security().getApiKey(getApiKeyRequest, RequestOptions.DEFAULT);
+
+            assertThat(getApiKeyResponse.getApiKeyInfos(), is(notNullValue()));
+            assertThat(getApiKeyResponse.getApiKeyInfos().size(), is(1));
+            verifyApiKey(getApiKeyResponse.getApiKeyInfos().get(0), expectedApiKeyInfo);
+        }
+
         {
             // tag::get-user-realm-api-keys-request
             GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.usingRealmAndUserName("default_file", "test_user");
@@ -1980,7 +2000,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
         }
 
         {
-            GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.usingApiKeyId(createApiKeyResponse1.getId());
+            GetApiKeyRequest getApiKeyRequest = GetApiKeyRequest.usingApiKeyId(createApiKeyResponse1.getId(), false);
 
             ActionListener<GetApiKeyResponse> listener;
             // tag::get-api-key-execute-listener
@@ -2041,7 +2061,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
 
         {
             // tag::invalidate-api-key-id-request
-            InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingApiKeyId(createApiKeyResponse1.getId());
+            InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingApiKeyId(createApiKeyResponse1.getId(), false);
             // end::invalidate-api-key-id-request
 
             // tag::invalidate-api-key-execute
@@ -2066,7 +2086,8 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             assertNotNull(createApiKeyResponse2.getKey());
 
             // tag::invalidate-api-key-name-request
-            InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingApiKeyName(createApiKeyResponse2.getName());
+            InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingApiKeyName(createApiKeyResponse2.getName(),
+                false);
             // end::invalidate-api-key-name-request
 
             InvalidateApiKeyResponse invalidateApiKeyResponse = client.security().invalidateApiKey(invalidateApiKeyRequest,
@@ -2159,7 +2180,7 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             assertThat(createApiKeyResponse6.getName(), equalTo("k6"));
             assertNotNull(createApiKeyResponse6.getKey());
 
-            InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingApiKeyId(createApiKeyResponse6.getId());
+            InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.usingApiKeyId(createApiKeyResponse6.getId(), false);
 
             ActionListener<InvalidateApiKeyResponse> listener;
             // tag::invalidate-api-key-execute-listener
@@ -2195,5 +2216,109 @@ public class SecurityDocumentationIT extends ESRestHighLevelClientTestCase {
             assertThat(invalidatedApiKeyIds, containsInAnyOrder(expectedInvalidatedApiKeyIds.toArray(Strings.EMPTY_ARRAY)));
             assertThat(response.getPreviouslyInvalidatedApiKeys().size(), equalTo(0));
         }
+
+        {
+            createApiKeyRequest = new CreateApiKeyRequest("k7", roles, expiration, refreshPolicy);
+            CreateApiKeyResponse createApiKeyResponse7 = client.security().createApiKey(createApiKeyRequest, RequestOptions.DEFAULT);
+            assertThat(createApiKeyResponse7.getName(), equalTo("k7"));
+            assertNotNull(createApiKeyResponse7.getKey());
+
+            // tag::invalidate-api-keys-owned-by-authenticated-user-request
+            InvalidateApiKeyRequest invalidateApiKeyRequest = InvalidateApiKeyRequest.forOwnedApiKeys();
+            // end::invalidate-api-keys-owned-by-authenticated-user-request
+
+            InvalidateApiKeyResponse invalidateApiKeyResponse = client.security().invalidateApiKey(invalidateApiKeyRequest,
+                RequestOptions.DEFAULT);
+
+            final List<ElasticsearchException> errors = invalidateApiKeyResponse.getErrors();
+            final List<String> invalidatedApiKeyIds = invalidateApiKeyResponse.getInvalidatedApiKeys();
+            final List<String> previouslyInvalidatedApiKeyIds = invalidateApiKeyResponse.getPreviouslyInvalidatedApiKeys();
+
+            assertTrue(errors.isEmpty());
+            List<String> expectedInvalidatedApiKeyIds = Arrays.asList(createApiKeyResponse7.getId());
+            assertThat(invalidatedApiKeyIds, containsInAnyOrder(expectedInvalidatedApiKeyIds.toArray(Strings.EMPTY_ARRAY)));
+            assertThat(previouslyInvalidatedApiKeyIds.size(), equalTo(0));
+        }
+
+    }
+
+    public void testDelegatePkiAuthentication() throws Exception {
+        final RestHighLevelClient client = highLevelClient();
+        X509Certificate clientCertificate = readCertForPkiDelegation("testClient.crt");
+        X509Certificate intermediateCA = readCertForPkiDelegation("testIntermediateCA.crt");
+        {
+            //tag::delegate-pki-request
+            DelegatePkiAuthenticationRequest request = new DelegatePkiAuthenticationRequest(
+                    Arrays.asList(clientCertificate, intermediateCA));
+            //end::delegate-pki-request
+            //tag::delegate-pki-execute
+            DelegatePkiAuthenticationResponse response = client.security().delegatePkiAuthentication(request, RequestOptions.DEFAULT);
+            //end::delegate-pki-execute
+            //tag::delegate-pki-response
+            String accessToken = response.getAccessToken(); // <1>
+            //end::delegate-pki-response
+
+            RequestOptions.Builder optionsBuilder = RequestOptions.DEFAULT.toBuilder();
+            optionsBuilder.addHeader("Authorization", "Bearer " + accessToken);
+            AuthenticateResponse resp = client.security().authenticate(optionsBuilder.build());
+            User user = resp.getUser();
+            assertThat(user, is(notNullValue()));
+            assertThat(user.getUsername(), is("Elasticsearch Test Client"));
+            RealmInfo authnRealm = resp.getAuthenticationRealm();
+            assertThat(authnRealm, is(notNullValue()));
+            assertThat(authnRealm.getName(), is("pki1"));
+            assertThat(authnRealm.getType(), is("pki"));
+        }
+
+        {
+            DelegatePkiAuthenticationRequest request = new DelegatePkiAuthenticationRequest(
+                    Arrays.asList(clientCertificate, intermediateCA));
+            ActionListener<DelegatePkiAuthenticationResponse> listener;
+
+            //tag::delegate-pki-execute-listener
+            listener = new ActionListener<DelegatePkiAuthenticationResponse>() {
+                @Override
+                public void onResponse(DelegatePkiAuthenticationResponse getRolesResponse) {
+                    // <1>
+                }
+
+                @Override
+                public void onFailure(Exception e) {
+                    // <2>
+                }
+            };
+            //end::delegate-pki-execute-listener
+
+            assertNotNull(listener);
+
+            // Replace the empty listener by a blocking listener in test
+            final PlainActionFuture<DelegatePkiAuthenticationResponse> future = new PlainActionFuture<>();
+            listener = future;
+
+            //tag::delegate-pki-execute-async
+            client.security().delegatePkiAuthenticationAsync(request, RequestOptions.DEFAULT, listener); // <1>
+            //end::delegate-pki-execute-async
+
+            final DelegatePkiAuthenticationResponse response = future.get(30, TimeUnit.SECONDS);
+            String accessToken = response.getAccessToken();
+            RequestOptions.Builder optionsBuilder = RequestOptions.DEFAULT.toBuilder();
+            optionsBuilder.addHeader("Authorization", "Bearer " + accessToken);
+            AuthenticateResponse resp = client.security().authenticate(optionsBuilder.build());
+            User user = resp.getUser();
+            assertThat(user, is(notNullValue()));
+            assertThat(user.getUsername(), is("Elasticsearch Test Client"));
+            RealmInfo authnRealm = resp.getAuthenticationRealm();
+            assertThat(authnRealm, is(notNullValue()));
+            assertThat(authnRealm.getName(), is("pki1"));
+            assertThat(authnRealm.getType(), is("pki"));
+        }
+    }
+
+    private X509Certificate readCertForPkiDelegation(String certificateName) throws Exception {
+        Path path = getDataPath("/org/elasticsearch/client/security/delegate_pki/" + certificateName);
+        try (InputStream in = Files.newInputStream(path)) {
+            CertificateFactory factory = CertificateFactory.getInstance("X.509");
+            return (X509Certificate) factory.generateCertificate(in);
+        }
     }
 }

+ 14 - 3
client/rest-high-level/src/test/java/org/elasticsearch/client/ml/dataframe/DataFrameAnalyticsStatsTests.java

@@ -24,6 +24,8 @@ import org.elasticsearch.common.xcontent.XContentBuilder;
 import org.elasticsearch.test.ESTestCase;
 
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
 
 import static org.elasticsearch.test.AbstractXContentTestCase.xContentTester;
 
@@ -44,11 +46,20 @@ public class DataFrameAnalyticsStatsTests extends ESTestCase {
             randomAlphaOfLengthBetween(1, 10),
             randomFrom(DataFrameAnalyticsState.values()),
             randomBoolean() ? null : randomAlphaOfLength(10),
-            randomBoolean() ? null : randomIntBetween(0, 100),
+            randomBoolean() ? null : createRandomProgress(),
             randomBoolean() ? null : NodeAttributesTests.createRandom(),
             randomBoolean() ? null : randomAlphaOfLengthBetween(1, 20));
     }
 
+    private static List<PhaseProgress> createRandomProgress() {
+        int progressPhaseCount = randomIntBetween(3, 7);
+        List<PhaseProgress> progress = new ArrayList<>(progressPhaseCount);
+        for (int i = 0; i < progressPhaseCount; i++) {
+            progress.add(new PhaseProgress(randomAlphaOfLength(20), randomIntBetween(0, 100)));
+        }
+        return progress;
+    }
+
     public static void toXContent(DataFrameAnalyticsStats stats, XContentBuilder builder) throws IOException {
         builder.startObject();
         builder.field(DataFrameAnalyticsStats.ID.getPreferredName(), stats.getId());
@@ -56,8 +67,8 @@ public class DataFrameAnalyticsStatsTests extends ESTestCase {
         if (stats.getFailureReason() != null) {
             builder.field(DataFrameAnalyticsStats.FAILURE_REASON.getPreferredName(), stats.getFailureReason());
         }
-        if (stats.getProgressPercent() != null) {
-            builder.field(DataFrameAnalyticsStats.PROGRESS_PERCENT.getPreferredName(), stats.getProgressPercent());
+        if (stats.getProgress() != null) {
+            builder.field(DataFrameAnalyticsStats.PROGRESS.getPreferredName(), stats.getProgress());
         }
         if (stats.getNode() != null) {
             builder.field(DataFrameAnalyticsStats.NODE.getPreferredName(), stats.getNode());

+ 46 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/ml/dataframe/PhaseProgressTests.java

@@ -0,0 +1,46 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.elasticsearch.client.ml.dataframe;
+
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.test.AbstractXContentTestCase;
+
+import java.io.IOException;
+
+public class PhaseProgressTests extends AbstractXContentTestCase<PhaseProgress> {
+
+    public static PhaseProgress createRandom() {
+        return new PhaseProgress(randomAlphaOfLength(20), randomIntBetween(0, 100));
+    }
+
+    @Override
+    protected PhaseProgress createTestInstance() {
+        return createRandom();
+    }
+
+    @Override
+    protected PhaseProgress doParseInstance(XContentParser parser) throws IOException {
+        return PhaseProgress.PARSER.apply(parser, null);
+    }
+
+    @Override
+    protected boolean supportsUnknownFields() {
+        return true;
+    }
+}

+ 54 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/ml/dataframe/RegressionTests.java

@@ -0,0 +1,54 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.elasticsearch.client.ml.dataframe;
+
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.test.AbstractXContentTestCase;
+
+import java.io.IOException;
+
+public class RegressionTests extends AbstractXContentTestCase<Regression> {
+
+    public static Regression randomRegression() {
+        return Regression.builder(randomAlphaOfLength(10))
+            .setLambda(randomBoolean() ? null : randomDoubleBetween(0.0, Double.MAX_VALUE, true))
+            .setGamma(randomBoolean() ? null : randomDoubleBetween(0.0, Double.MAX_VALUE, true))
+            .setEta(randomBoolean() ? null : randomDoubleBetween(0.001, 1.0, true))
+            .setMaximumNumberTrees(randomBoolean() ? null : randomIntBetween(1, 2000))
+            .setFeatureBagFraction(randomBoolean() ? null : randomDoubleBetween(0.0, 1.0, false))
+            .setPredictionFieldName(randomBoolean() ? null : randomAlphaOfLength(10))
+            .setTrainingPercent(randomBoolean() ? null : randomDoubleBetween(1.0, 100.0, true))
+            .build();
+    }
+
+    @Override
+    protected Regression createTestInstance() {
+        return randomRegression();
+    }
+
+    @Override
+    protected Regression doParseInstance(XContentParser parser) throws IOException {
+        return Regression.fromXContent(parser);
+    }
+
+    @Override
+    protected boolean supportsUnknownFields() {
+        return true;
+    }
+}

+ 107 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/security/DelegatePkiAuthenticationRequestTests.java

@@ -0,0 +1,107 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.client.security;
+
+import org.elasticsearch.client.AbstractRequestTestCase;
+import org.elasticsearch.client.ValidationException;
+import org.elasticsearch.common.xcontent.XContentParser;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+
+import javax.security.auth.x500.X500Principal;
+
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DelegatePkiAuthenticationRequestTests extends AbstractRequestTestCase<DelegatePkiAuthenticationRequest,
+        org.elasticsearch.xpack.core.security.action.DelegatePkiAuthenticationRequest> {
+
+    public void testEmptyOrNullCertificateChain() throws Exception {
+        IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> {
+            new DelegatePkiAuthenticationRequest((List<X509Certificate>)null);
+        });
+        assertThat(e.getMessage(), is("certificate chain must not be empty or null"));
+        e = expectThrows(IllegalArgumentException.class, () -> {
+            new DelegatePkiAuthenticationRequest(Collections.emptyList());
+        });
+        assertThat(e.getMessage(), is("certificate chain must not be empty or null"));
+    }
+
+    public void testUnorderedCertificateChain() throws Exception {
+        List<X509Certificate> mockCertChain = new ArrayList<>(2);
+        mockCertChain.add(mock(X509Certificate.class));
+        when(mockCertChain.get(0).getIssuerX500Principal()).thenReturn(new X500Principal("CN=Test, OU=elasticsearch, O=org"));
+        mockCertChain.add(mock(X509Certificate.class));
+        when(mockCertChain.get(1).getSubjectX500Principal()).thenReturn(new X500Principal("CN=Not Test, OU=elasticsearch, O=org"));
+        DelegatePkiAuthenticationRequest request = new DelegatePkiAuthenticationRequest(mockCertChain);
+        Optional<ValidationException> ve = request.validate();
+        assertThat(ve.isPresent(), is(true));
+        assertThat(ve.get().validationErrors().size(), is(1));
+        assertThat(ve.get().validationErrors().get(0), is("certificates chain must be an ordered chain"));
+    }
+
+    @Override
+    protected DelegatePkiAuthenticationRequest createClientTestInstance() {
+        List<X509Certificate> certificates = randomCertificateList();
+        return new DelegatePkiAuthenticationRequest(certificates);
+    }
+
+    @Override
+    protected org.elasticsearch.xpack.core.security.action.DelegatePkiAuthenticationRequest doParseToServerInstance(XContentParser parser)
+            throws IOException {
+        return org.elasticsearch.xpack.core.security.action.DelegatePkiAuthenticationRequest.fromXContent(parser);
+    }
+
+    @Override
+    protected void assertInstances(org.elasticsearch.xpack.core.security.action.DelegatePkiAuthenticationRequest serverInstance,
+            DelegatePkiAuthenticationRequest clientTestInstance) {
+        assertThat(serverInstance.getCertificateChain(), is(clientTestInstance.getCertificateChain()));
+    }
+
+    private List<X509Certificate> randomCertificateList() {
+        List<X509Certificate> certificates = Arrays.asList(randomArray(1, 3, X509Certificate[]::new, () -> {
+            try {
+                return readCertForPkiDelegation(randomFrom("testClient.crt", "testIntermediateCA.crt", "testRootCA.crt"));
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+        }));
+        return certificates;
+    }
+
+    private X509Certificate readCertForPkiDelegation(String certificateName) throws Exception {
+        Path path = getDataPath("/org/elasticsearch/client/security/delegate_pki/" + certificateName);
+        try (InputStream in = Files.newInputStream(path)) {
+            CertificateFactory factory = CertificateFactory.getInstance("X.509");
+            return (X509Certificate) factory.generateCertificate(in);
+        }
+    }
+}

+ 53 - 0
client/rest-high-level/src/test/java/org/elasticsearch/client/security/DelegatePkiAuthenticationResponseTests.java

@@ -0,0 +1,53 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you under
+ * the Apache License, Version 2.0 (the "License"); you may
+ * not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.elasticsearch.client.security;
+
+import org.elasticsearch.client.AbstractResponseTestCase;
+import org.elasticsearch.common.unit.TimeValue;
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.client.security.DelegatePkiAuthenticationResponse;
+
+import java.io.IOException;
+
+import static org.hamcrest.Matchers.is;
+
+public class DelegatePkiAuthenticationResponseTests extends
+    AbstractResponseTestCase<org.elasticsearch.xpack.core.security.action.DelegatePkiAuthenticationResponse,
+        DelegatePkiAuthenticationResponse> {
+
+    @Override
+    protected org.elasticsearch.xpack.core.security.action.DelegatePkiAuthenticationResponse createServerTestInstance() {
+        return new org.elasticsearch.xpack.core.security.action.DelegatePkiAuthenticationResponse(randomAlphaOfLength(6),
+                TimeValue.parseTimeValue(randomTimeValue(), getClass().getSimpleName() + ".expiresIn"));
+    }
+
+    @Override
+    protected DelegatePkiAuthenticationResponse doParseToClientInstance(XContentParser parser) throws IOException {
+        return DelegatePkiAuthenticationResponse.fromXContent(parser);
+    }
+
+    @Override
+    protected void assertInstances(org.elasticsearch.xpack.core.security.action.DelegatePkiAuthenticationResponse serverTestInstance,
+            DelegatePkiAuthenticationResponse clientInstance) {
+        assertThat(serverTestInstance.getAccessToken(), is(clientInstance.getAccessToken()));
+        assertThat(serverTestInstance.getExpiresIn(), is(clientInstance.getExpiresIn()));
+        assertThat(clientInstance.getType(), is("Bearer"));
+    }
+}

+ 23 - 11
client/rest-high-level/src/test/java/org/elasticsearch/client/security/GetApiKeyRequestTests.java

@@ -30,10 +30,10 @@ import static org.hamcrest.Matchers.equalTo;
 public class GetApiKeyRequestTests extends ESTestCase {
 
     public void testRequestValidation() {
-        GetApiKeyRequest request = GetApiKeyRequest.usingApiKeyId(randomAlphaOfLength(5));
+        GetApiKeyRequest request = GetApiKeyRequest.usingApiKeyId(randomAlphaOfLength(5), randomBoolean());
         Optional<ValidationException> ve = request.validate();
         assertFalse(ve.isPresent());
-        request = GetApiKeyRequest.usingApiKeyName(randomAlphaOfLength(5));
+        request = GetApiKeyRequest.usingApiKeyName(randomAlphaOfLength(5), randomBoolean());
         ve = request.validate();
         assertFalse(ve.isPresent());
         request = GetApiKeyRequest.usingRealmName(randomAlphaOfLength(5));
@@ -45,28 +45,40 @@ public class GetApiKeyRequestTests extends ESTestCase {
         request = GetApiKeyRequest.usingRealmAndUserName(randomAlphaOfLength(5), randomAlphaOfLength(7));
         ve = request.validate();
         assertFalse(ve.isPresent());
+        request = GetApiKeyRequest.forOwnedApiKeys();
+        ve = request.validate();
+        assertFalse(ve.isPresent());
     }
 
     public void testRequestValidationFailureScenarios() throws IOException {
         String[][] inputs = new String[][] {
-                { randomFrom(new String[] { null, "" }), randomFrom(new String[] { null, "" }), randomFrom(new String[] { null, "" }),
-                        randomFrom(new String[] { null, "" }) },
-                { randomFrom(new String[] { null, "" }), "user", "api-kid", "api-kname" },
-                { "realm", randomFrom(new String[] { null, "" }), "api-kid", "api-kname" },
-                { "realm", "user", "api-kid", randomFrom(new String[] { null, "" }) },
-                { randomFrom(new String[] { null, "" }), randomFrom(new String[] { null, "" }), "api-kid", "api-kname" } };
-        String[] expectedErrorMessages = new String[] { "One of [api key id, api key name, username, realm name] must be specified",
+                { randomNullOrEmptyString(), randomNullOrEmptyString(), randomNullOrEmptyString(), randomNullOrEmptyString(), "false" },
+                { randomNullOrEmptyString(), "user", "api-kid", "api-kname", "false" },
+                { "realm", randomNullOrEmptyString(), "api-kid", "api-kname", "false" },
+                { "realm", "user", "api-kid", randomNullOrEmptyString(), "false" },
+                { randomNullOrEmptyString(), randomNullOrEmptyString(), "api-kid", "api-kname", "false" },
+                { "realm", randomNullOrEmptyString(), randomNullOrEmptyString(), randomNullOrEmptyString(), "true"},
+                { randomNullOrEmptyString(), "user", randomNullOrEmptyString(), randomNullOrEmptyString(), "true"} };
+        String[] expectedErrorMessages = new String[] {
+                "One of [api key id, api key name, username, realm name] must be specified if [owner] flag is false",
                 "username or realm name must not be specified when the api key id or api key name is specified",
                 "username or realm name must not be specified when the api key id or api key name is specified",
                 "username or realm name must not be specified when the api key id or api key name is specified",
-                "only one of [api key id, api key name] can be specified" };
+                "only one of [api key id, api key name] can be specified",
+                "neither username nor realm-name may be specified when retrieving owned API keys",
+                "neither username nor realm-name may be specified when retrieving owned API keys" };
 
         for (int i = 0; i < inputs.length; i++) {
             final int caseNo = i;
             IllegalArgumentException ve = expectThrows(IllegalArgumentException.class,
-                    () -> new GetApiKeyRequest(inputs[caseNo][0], inputs[caseNo][1], inputs[caseNo][2], inputs[caseNo][3]));
+                    () -> new GetApiKeyRequest(inputs[caseNo][0], inputs[caseNo][1], inputs[caseNo][2], inputs[caseNo][3],
+                        Boolean.valueOf(inputs[caseNo][4])));
             assertNotNull(ve);
             assertThat(ve.getMessage(), equalTo(expectedErrorMessages[caseNo]));
         }
     }
+
+    private static String randomNullOrEmptyString() {
+        return randomBoolean() ? "" : null;
+    }
 }

+ 23 - 11
client/rest-high-level/src/test/java/org/elasticsearch/client/security/InvalidateApiKeyRequestTests.java

@@ -31,10 +31,10 @@ import static org.hamcrest.Matchers.is;
 public class InvalidateApiKeyRequestTests extends ESTestCase {
 
     public void testRequestValidation() {
-        InvalidateApiKeyRequest request = InvalidateApiKeyRequest.usingApiKeyId(randomAlphaOfLength(5));
+        InvalidateApiKeyRequest request = InvalidateApiKeyRequest.usingApiKeyId(randomAlphaOfLength(5), randomBoolean());
         Optional<ValidationException> ve = request.validate();
         assertThat(ve.isPresent(), is(false));
-        request = InvalidateApiKeyRequest.usingApiKeyName(randomAlphaOfLength(5));
+        request = InvalidateApiKeyRequest.usingApiKeyName(randomAlphaOfLength(5), randomBoolean());
         ve = request.validate();
         assertThat(ve.isPresent(), is(false));
         request = InvalidateApiKeyRequest.usingRealmName(randomAlphaOfLength(5));
@@ -46,28 +46,40 @@ public class InvalidateApiKeyRequestTests extends ESTestCase {
         request = InvalidateApiKeyRequest.usingRealmAndUserName(randomAlphaOfLength(5), randomAlphaOfLength(7));
         ve = request.validate();
         assertThat(ve.isPresent(), is(false));
+        request = InvalidateApiKeyRequest.forOwnedApiKeys();
+        ve = request.validate();
+        assertFalse(ve.isPresent());
     }
 
     public void testRequestValidationFailureScenarios() throws IOException {
         String[][] inputs = new String[][] {
-                { randomFrom(new String[] { null, "" }), randomFrom(new String[] { null, "" }), randomFrom(new String[] { null, "" }),
-                        randomFrom(new String[] { null, "" }) },
-                { randomFrom(new String[] { null, "" }), "user", "api-kid", "api-kname" },
-                { "realm", randomFrom(new String[] { null, "" }), "api-kid", "api-kname" },
-                { "realm", "user", "api-kid", randomFrom(new String[] { null, "" }) },
-                { randomFrom(new String[] { null, "" }), randomFrom(new String[] { null, "" }), "api-kid", "api-kname" } };
-        String[] expectedErrorMessages = new String[] { "One of [api key id, api key name, username, realm name] must be specified",
+                { randomNullOrEmptyString(), randomNullOrEmptyString(), randomNullOrEmptyString(), randomNullOrEmptyString(), "false" },
+                { randomNullOrEmptyString(), "user", "api-kid", "api-kname", "false" },
+                { "realm", randomNullOrEmptyString(), "api-kid", "api-kname", "false" },
+                { "realm", "user", "api-kid", randomNullOrEmptyString(), "false" },
+                { randomNullOrEmptyString(), randomNullOrEmptyString(), "api-kid", "api-kname", "false" },
+                { "realm", randomNullOrEmptyString(), randomNullOrEmptyString(), randomNullOrEmptyString(), "true" },
+                { randomNullOrEmptyString(), "user", randomNullOrEmptyString(), randomNullOrEmptyString(), "true" } };
+        String[] expectedErrorMessages = new String[] {
+                "One of [api key id, api key name, username, realm name] must be specified if [owner] flag is false",
                 "username or realm name must not be specified when the api key id or api key name is specified",
                 "username or realm name must not be specified when the api key id or api key name is specified",
                 "username or realm name must not be specified when the api key id or api key name is specified",
-                "only one of [api key id, api key name] can be specified" };
+                "only one of [api key id, api key name] can be specified",
+                "neither username nor realm-name may be specified when invalidating owned API keys",
+                "neither username nor realm-name may be specified when invalidating owned API keys" };
 
         for (int i = 0; i < inputs.length; i++) {
             final int caseNo = i;
             IllegalArgumentException ve = expectThrows(IllegalArgumentException.class,
-                    () -> new InvalidateApiKeyRequest(inputs[caseNo][0], inputs[caseNo][1], inputs[caseNo][2], inputs[caseNo][3]));
+                    () -> new InvalidateApiKeyRequest(inputs[caseNo][0], inputs[caseNo][1], inputs[caseNo][2], inputs[caseNo][3],
+                        Boolean.valueOf(inputs[caseNo][4])));
             assertNotNull(ve);
             assertThat(ve.getMessage(), equalTo(expectedErrorMessages[caseNo]));
         }
     }
+
+    private static String randomNullOrEmptyString() {
+        return randomBoolean() ? "" : null;
+    }
 }

+ 35 - 0
client/rest-high-level/src/test/resources/org/elasticsearch/client/security/delegate_pki/README.asciidoc

@@ -0,0 +1,35 @@
+= Certificate Chain details
+This document details the steps used to create the certificate chain in this directory.
+The chain has a length of 3: the Root CA, the Intermediate CA and the Client Certificate.
+All openssl commands use the same configuration file, albeit different sections of it.
+The OpenSSL Configuration file is located in this directory as `openssl_config.cnf`.
+
+== Instructions on generating self-signed Root CA
+The self-signed Root CA, 'testRootCA.crt', and its associated private key in this directory
+have been generated using the following openssl commands.
+
+[source,shell]
+-----------------------------------------------------------------------------------------------------------
+openssl genrsa -out testRootCA.key 2048
+openssl req -x509 -new -key testRootCA.key -days 1460 -subj "/CN=Elasticsearch Test Root CA/OU=elasticsearch/O=org" -out testRootCA.crt -config ./openssl_config.cnf
+-----------------------------------------------------------------------------------------------------------
+
+== Instructions on generating the Intermediate CA
+The `testIntermediateCA.crt` CA certificate is "issued" by the `testRootCA.crt`.
+
+[source,shell]
+-----------------------------------------------------------------------------------------------------------
+openssl genrsa -out testIntermediateCA.key 2048
+openssl req -new -key testIntermediateCA.key -subj "/CN=Elasticsearch Test Intermediate CA/OU=Elasticsearch/O=org" -out testIntermediateCA.csr -config ./openssl_config.cnf
+openssl x509 -req -in testIntermediateCA.csr -CA testRootCA.crt -CAkey testRootCA.key -CAcreateserial -out testIntermediateCA.crt -days 1460 -sha256 -extensions v3_ca -extfile ./openssl_config.cnf
+-----------------------------------------------------------------------------------------------------------
+
+== Instructions on generating the Client Certificate
+The `testClient.crt` end entity certificate is "issued" by the `testIntermediateCA.crt`.
+
+[source,shell]
+-----------------------------------------------------------------------------------------------------------
+openssl genrsa -out testClient.key 2048
+openssl req -new -key testClient.key -subj "/CN=Elasticsearch Test Client/OU=Elasticsearch/O=org" -out testClient.csr -config ./openssl_config.cnf
+openssl x509 -req -in testClient.csr -CA testIntermediateCA.crt -CAkey testIntermediateCA.key -CAcreateserial -out testClient.crt -days 1460 -sha256 -extensions usr_cert -extfile ./openssl_config.cnf
+-----------------------------------------------------------------------------------------------------------

+ 185 - 0
client/rest-high-level/src/test/resources/org/elasticsearch/client/security/delegate_pki/openssl_config.cnf

@@ -0,0 +1,185 @@
+####################################################################
+# CA Definition
+[ ca ]
+default_ca      = CA_default            # The default ca section
+
+####################################################################
+# Per the above, this is where we define CA values
+[ CA_default ]
+
+# By default we use "user certificate" extensions when signing
+x509_extensions = usr_cert              # The extentions to add to the cert
+
+# Honor extensions requested of us
+copy_extensions    = copy
+
+# Comment out the following two lines for the "traditional"
+# (and highly broken) format.
+name_opt        = ca_default            # Subject Name options
+cert_opt        = ca_default            # Certificate field options
+
+# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
+# so this is commented out by default to leave a V1 CRL.
+# crlnumber must also be commented out to leave a V1 CRL.
+#crl_extensions        = crl_ext
+default_days    = 1460                  # how long to certify for
+default_md      = sha256                # which md to use.
+preserve        = no                    # keep passed DN ordering
+
+# A few difference way of specifying how similar the request should look
+# For type CA, the listed attributes must be the same, and the optional
+# and supplied fields are just that :-)
+policy          = policy_anything
+
+####################################################################
+# The default policy for the CA when signing requests, requires some
+# resemblence to the CA cert
+#
+[ policy_match ]
+countryName             = match         # Must be the same as the CA
+stateOrProvinceName     = match         # Must be the same as the CA
+organizationName        = match         # Must be the same as the CA
+organizationalUnitName  = optional      # not required
+commonName              = supplied      # must be there, whatever it is
+emailAddress            = optional      # not required
+
+####################################################################
+# An alternative policy not referred to anywhere in this file. Can
+# be used by specifying '-policy policy_anything' to ca(8).
+#
+[ policy_anything ]
+countryName             = optional
+stateOrProvinceName     = optional
+localityName            = optional
+organizationName        = optional
+organizationalUnitName  = optional
+commonName              = supplied
+emailAddress            = optional
+
+####################################################################
+# This is where we define how to generate CSRs
+[ req ]
+default_bits            = 2048
+default_keyfile         = privkey.pem
+distinguished_name      = req_distinguished_name # where to get DN for reqs
+attributes              = req_attributes         # req attributes
+x509_extensions         = v3_ca  # The extentions to add to self signed certs
+req_extensions          = v3_req # The extensions to add to req's
+
+# This sets a mask for permitted string types. There are several options.
+# default: PrintableString, T61String, BMPString.
+# pkix   : PrintableString, BMPString.
+# utf8only: only UTF8Strings.
+# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
+# MASK:XXXX a literal mask value.
+# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
+# so use this option with caution!
+string_mask = nombstr
+
+
+####################################################################
+# Per "req" section, this is where we define DN info
+[ req_distinguished_name ]
+
+0.organizationName              = Organization Name (company)
+0.organizationName_default      = org
+
+organizationalUnitName          = Organizational Unit Name (eg, section)
+organizationalUnitName_default  = elasticsearch
+
+commonName                      = Common Name (hostname, IP, or your name)
+commonName_default              = Elasticsearch Test Certificate
+commonName_max = 64
+
+####################################################################
+# We don't want these, but the section must exist
+[ req_attributes ]
+#challengePassword              = A challenge password
+#challengePassword_min          = 4
+#challengePassword_max          = 20
+#unstructuredName               = An optional company name
+
+
+####################################################################
+# Extensions for when we sign normal certs (specified as default)
+[ usr_cert ]
+
+# User certs aren't CAs, by definition
+basicConstraints=CA:false
+
+# Here are some examples of the usage of nsCertType. If it is omitted
+# the certificate can be used for anything *except* object signing.
+# This is OK for an SSL server.
+#nsCertType = server
+# For an object signing certificate this would be used.
+#nsCertType = objsign
+# For normal client use this is typical
+#nsCertType = client, email
+# and for everything including object signing:
+#nsCertType = client, email, objsign
+# This is typical in keyUsage for a client certificate.
+#keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+
+# PKIX recommendations harmless if included in all certificates.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+# This stuff is for subjectAltName and issuerAltname.
+# Import the email address.
+#subjectAltName=email:copy
+# An alternative to produce certificates that aren't
+# deprecated according to PKIX.
+#subjectAltName=email:move
+
+
+####################################################################
+# Extension for requests
+[ v3_req ]
+basicConstraints = CA:FALSE
+
+# PKIX recommendation.
+subjectKeyIdentifier = hash
+
+subjectAltName = @alt_names
+
+####################################################################
+# An alternative section of extensions, not referred to anywhere
+# else in the config. We'll use this via '-extensions v3_ca' when
+# using ca(8) to sign another CA.
+#
+[ v3_ca ]
+
+# PKIX recommendation.
+subjectKeyIdentifier=hash
+authorityKeyIdentifier = keyid,issuer
+
+# This is what PKIX recommends but some broken software chokes on critical
+# extensions.
+#basicConstraints = critical,CA:true
+# So we do this instead.
+basicConstraints = CA:true
+
+# Key usage: this is typical for a CA certificate. However since it will
+# prevent it being used as an test self-signed certificate it is best
+# left out by default.
+# keyUsage = cRLSign, keyCertSign
+
+# Some might want this also
+# nsCertType = sslCA, emailCA
+
+# Include email address in subject alt name: another PKIX recommendation
+#subjectAltName=email:move
+# Copy issuer details
+#issuerAltName=issuer:copy
+
+subjectAltName = @alt_names
+
+[ alt_names ]
+DNS.1 = localhost
+DNS.2 = localhost.localdomain
+DNS.3 = localhost4
+DNS.4 = localhost4.localdomain4
+DNS.5 = localhost6
+DNS.6 = localhost6.localdomain6
+IP.1 = 127.0.0.1
+IP.2 = ::1

+ 21 - 0
client/rest-high-level/src/test/resources/org/elasticsearch/client/security/delegate_pki/testClient.crt

@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE-----
+MIIDbTCCAlWgAwIBAgIJAIxTS7Qdho9jMA0GCSqGSIb3DQEBCwUAMFMxKzApBgNV
+BAMTIkVsYXN0aWNzZWFyY2ggVGVzdCBJbnRlcm1lZGlhdGUgQ0ExFjAUBgNVBAsT
+DUVsYXN0aWNzZWFyY2gxDDAKBgNVBAoTA29yZzAeFw0xOTA3MTkxMzMzNDFaFw0y
+MzA3MTgxMzMzNDFaMEoxIjAgBgNVBAMTGUVsYXN0aWNzZWFyY2ggVGVzdCBDbGll
+bnQxFjAUBgNVBAsTDUVsYXN0aWNzZWFyY2gxDDAKBgNVBAoTA29yZzCCASIwDQYJ
+KoZIhvcNAQEBBQADggEPADCCAQoCggEBANHgMX2aX8t0nj4sGLNuKISmmXIYCj9R
+wRqS7L03l9Nng7kOKnhHu/nXDt7zMRJyHj+q6FAt5khlavYSVCQyrDybRuA5z31g
+OdqXerrjs2OXS5HSHNvoDAnHFsaYX/5geMewVTtc/vqpd7Ph/QtaKfmG2FK0JNQo
+0k24tcgCIcyMtBh6BA70yGBM0OT8GdOgd/d/mA7mRhaxIUMNYQzRYRsp4hMnnWoO
+TkR5Q8KSO3MKw9dPSpPe8EnwtJE10S3s5aXmgytru/xQqrFycPBNj4KbKVmqMP0G
+60CzXik5pr2LNvOFz3Qb6sYJtqeZF+JKgGWdaTC89m63+TEnUHqk0lcCAwEAAaNN
+MEswCQYDVR0TBAIwADAdBgNVHQ4EFgQU/+aAD6Q4mFq1vpHorC25/OY5zjcwHwYD
+VR0jBBgwFoAU8siFCiMiYZZm/95qFC75AG/LRE0wDQYJKoZIhvcNAQELBQADggEB
+AIRpCgDLpvXcgDHUk10uhxev21mlIbU+VP46ANnCuj0UELhTrdTuWvO1PAI4z+Wb
+DUxryQfOOXO9R6D0dE5yR56L/J7d+KayW34zU7yRDZM7+rXpocdQ1Ex8mjP9HJ/B
+f56YZTBQJpXeDrKow4FvtkI3bcIMkqmbG16LHQXeG3RS4ds4S4wCnE2nA6vIn9y+
+4R999q6y1VSBORrYULcDWxS54plHLEdiMr1vVallg82AGobS9GMcTL2U4Nx5IYZG
+7sbTk3LrDxVpVg/S2wLofEdOEwqCeHug/iOihNLJBabEW6z4TDLJAVW5KCY1Dfhk
+YlBfHn7vxKkfKoCUK/yLWWI=
+-----END CERTIFICATE-----

+ 27 - 0
client/rest-high-level/src/test/resources/org/elasticsearch/client/security/delegate_pki/testClient.key

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEA0eAxfZpfy3SePiwYs24ohKaZchgKP1HBGpLsvTeX02eDuQ4q
+eEe7+dcO3vMxEnIeP6roUC3mSGVq9hJUJDKsPJtG4DnPfWA52pd6uuOzY5dLkdIc
+2+gMCccWxphf/mB4x7BVO1z++ql3s+H9C1op+YbYUrQk1CjSTbi1yAIhzIy0GHoE
+DvTIYEzQ5PwZ06B393+YDuZGFrEhQw1hDNFhGyniEyedag5ORHlDwpI7cwrD109K
+k97wSfC0kTXRLezlpeaDK2u7/FCqsXJw8E2PgpspWaow/QbrQLNeKTmmvYs284XP
+dBvqxgm2p5kX4kqAZZ1pMLz2brf5MSdQeqTSVwIDAQABAoIBAQDAjP767Ioc4LZZ
+9h0HafaUlUDMs4+bPkd7OPcoNnv+AceRHZULW0zz0EIdfGM2OCrWYNfYz/Op0hpK
+/s/hkfgBdriU+ZUKwyDxEu8Pzd6EbYdwlqPRgdihk92qgJv5hsro8jeQSibJFHf1
+Ok3tf2BpRTTs08fCOl2P3vowMPyPa5Ho9bf4lzP8IsR2BZvoaev3za9ZWR6ZDzE6
+EWkBBNgIU4aPn1IJ6dz2+rVtN6+xXET0eYSBEac3xMQaPWLEX0EDBYPW1d+mUva/
+3lJvTrs3g8oyiTyVu0l9Yxdgox1mtgmrqqwxJ6XuouzImuXMMDXaz0K/E/+u2yPF
+V6kRvWuJAoGBAPOnEgBC3ezl+x+47cgbwpy97uZhZmV9HkMrSH9DKDwC+t57TdGX
+ypt2S/IS/vbPupFv0aHaWmJ6SN/HyTN4znwuulV3kE8mEpQzIPbluWfgQzT6ukJe
++YFI/+IXwIRBLA7khtfo01LGHSmLTENsnd/aoRySY3K6zJz36Ys3vFdjAoGBANyC
+7rF5YjPdgsAgOT7EboNGkc8UuW/Sh3xRp0c4Y+PBenf60yA5XkRJLYR4sZDjWTr0
+aKBY7Y8r+59U+bBrwUuhhoW08JZ/SBWja05+4DhH0ToA3vtbPv9lRyQfkF1DdBkn
+XpyM2vaJE5M454acwnKJ81AyoueYtZ8pD3Q7c219AoGAJ+F1wdMwDgGKvCOB0Boz
+HYK9IrpYj04OcQIZqLLuV/xI4befAiptQEr5nVLcprtTl1CNKIfb+Xh4iyBhX2pr
+qcngN/MNDNd3fQhtYdwyH72GYpqTeB+hiTbQo0ot+bfNJVbkd1ylkkvZJB6nyfVy
+VdysOEgBvRq0OREfCemCi28CgYEAoF1EE6NQDKICTZDhsMkQCb5PmcbbmPwFdh63
+xW64DlGNrCWoVt4BtS12wck4cUM1iE9oq3wgv6df5Z7ZuziSKVt9xk0xTnGgTcQ7
+7KkOjT+FZGZvw2K3bOsNkrK1vW2pyAU+pCE3uGU17DJNBjOIod27Kk649C61ntsw
+lvoJVs0CgYBLr9pzBRPyD5/lM9hm2EI7ITa+fVcu3V3bJfXENHKzpb0lB2fhl0PI
+swpiU8RUEKWyjBuHsdQdxg7AgFi/7s+SX7KLo4cudDRd73iiXYdNGB7R0/MAG8Jl
+/lMXn14noS4trA8fNGGg/2fANTBtLTbOX9i4s7clAo8ETywQ33owug==
+-----END RSA PRIVATE KEY-----

+ 24 - 0
client/rest-high-level/src/test/resources/org/elasticsearch/client/security/delegate_pki/testIntermediateCA.crt

@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIIEBTCCAu2gAwIBAgIJAIx9twpbtGkCMA0GCSqGSIb3DQEBCwUAMEsxIzAhBgNV
+BAMTGkVsYXN0aWNzZWFyY2ggVGVzdCBSb290IENBMRYwFAYDVQQLEw1lbGFzdGlj
+c2VhcmNoMQwwCgYDVQQKEwNvcmcwHhcNMTkwNzE5MTMzMjM0WhcNMjMwNzE4MTMz
+MjM0WjBTMSswKQYDVQQDEyJFbGFzdGljc2VhcmNoIFRlc3QgSW50ZXJtZWRpYXRl
+IENBMRYwFAYDVQQLEw1FbGFzdGljc2VhcmNoMQwwCgYDVQQKEwNvcmcwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCnJ2KTJZnQzOt0uUf+5oLNcvDLnnWY
+LzXZpOOX666Almwx+PVkDxkiGSe0QB9RWJqHSrsP1ryGIeCIzGMOctLt6QA7Peee
+HdrKqOQgN620nDSd2EZ3s0Iddh1Ns/lfTtBJCP/03suaktm7j8EYKAyOlTIUhiKm
+sTFlxPUSKjbtR4wR1ljnKN8X+j/ghr9mWhQrMR9rsGFObU8DQFho2Ti90C4HoMNU
+dy4j+2G3VVpaq4he4/4CbPrWQQ3dKGpzVAngIuAv4eQ/y88EHAFwutxQZWAew4Va
+5y3O112acSb9oC7g0NHQcBnos/WIChF5ki8V3LFnxN7jYvUUk9YxfA8hAgMBAAGj
+geMwgeAwHQYDVR0OBBYEFPLIhQojImGWZv/eahQu+QBvy0RNMB8GA1UdIwQYMBaA
+FM4SyNzpz82ihQ160zrLUVaWfI+1MAwGA1UdEwQFMAMBAf8wgY8GA1UdEQSBhzCB
+hIIJbG9jYWxob3N0ghVsb2NhbGhvc3QubG9jYWxkb21haW6CCmxvY2FsaG9zdDSC
+F2xvY2FsaG9zdDQubG9jYWxkb21haW40ggpsb2NhbGhvc3Q2ghdsb2NhbGhvc3Q2
+LmxvY2FsZG9tYWluNocEfwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0B
+AQsFAAOCAQEAMkh4nUi2yt5TX+ryBWaaA4/2ZOsxSeec5E1EjemPMUWGzFipV1YY
+k/mpv51E+BbPgtmGMG8Win/PETKYuX8D+zPauFEmJmyJmm5B4mr1406RWERqNDql
+36sOw89G0mDT/wIB4tkNdh830ml+d75aRVVB4X5pFAE8ZzI3g4OW4YxT3ZfUEhDl
+QeGVatobvIaX8KpNSevjFAFuQzSgj61VXI+2+UIRV4tJP2xEqu5ISuArHcGhvNlS
+bU3vZ80tTCa0tHyJrVqaqtQ23MDBzYPj6wJ/pvBQWAgZKnC3qJgXlJ9des117I1g
+J98AXCDGu5LBW/p2C9VpSktpnfzsX4NHqg==
+-----END CERTIFICATE-----

+ 27 - 0
client/rest-high-level/src/test/resources/org/elasticsearch/client/security/delegate_pki/testIntermediateCA.key

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEApydikyWZ0MzrdLlH/uaCzXLwy551mC812aTjl+uugJZsMfj1
+ZA8ZIhkntEAfUViah0q7D9a8hiHgiMxjDnLS7ekAOz3nnh3ayqjkIDettJw0ndhG
+d7NCHXYdTbP5X07QSQj/9N7LmpLZu4/BGCgMjpUyFIYiprExZcT1Eio27UeMEdZY
+5yjfF/o/4Ia/ZloUKzEfa7BhTm1PA0BYaNk4vdAuB6DDVHcuI/tht1VaWquIXuP+
+Amz61kEN3Shqc1QJ4CLgL+HkP8vPBBwBcLrcUGVgHsOFWuctztddmnEm/aAu4NDR
+0HAZ6LP1iAoReZIvFdyxZ8Te42L1FJPWMXwPIQIDAQABAoIBABp4z1C0dL6vpV5v
+9Wn2AaMd3+qvZro6R9H3HiAyMAmnSO1FGz/EcFuJFlOikBMm8BobCLMCdAreFJw1
+mj5wit0ouGOpcyQEYGEWDELZ7oWa825IESjl18OosA1dQlIIvk3Cwh56pk4NkbP1
+mUQFG6/9CthbQeOaTlNqtNEypE5Bc+JGbQaUhRP6tF+Rxnpys2nIJt/Vp9khw0Du
+K7Z6astunhfPDwLFGwHhflc6re1B+mxpLKTDHCcydJo2Kuh/LuuEtPkE5Ar4LwQk
+D+/61iZHC4B8/4IkBlAsgCJ1B18L6JdTbSYeVlepkSkJML5t6z+cvt5VcObF7F8X
+pPZn+kECgYEA2NaB0eshWNnHTMRv+sE92DCv0M7uV1eKtaopxOElAKJ/J2gpqcTh
+GzdTVRg1M2LgVNk97ViL5bsXaVStRe085m8oA0bI9WbIoQRUFp40dRFRUjl+4TN0
+pdxXL4VmQMWuwlO6p8/JY8sInnHVCT+2z8lek8P3bdtTQZV9OZQTn0kCgYEAxVe8
+obJdnUSXuRDWg588TW35PNqOTJcerIU6eRKwafvCcrhMoX62Xbv6y6kKXndW/JuW
+AbfSNiAOV+HGUbf8Xc54Xzk2mouoJA0S0tJ040jqOkFOaKIxYQudTU8y9bTXNsAk
+oX3wOhlt2q9xffAK1gYffP5XPXnYnsb8qaMIeRkCgYBM9yaxOgJmJTbGmtscaEbp
+W66sMScMPXhwruuQhFG7/fGgLSrMpaM5I9QiWitYB/qUY1/FxS4y5suSiYnPTjvV
+lxLexttBr6/65yxpstHv06vHwby1dqwqyyDvLyxyRTiYpVuVgP18vG5cvw7c746W
+BmXZkS9cAQN2Pfdq3pJwcQKBgEbCZd2owg5hCPIPyosZbpro4uRiDYIC8bm0b7n3
+7I+j+R3/XWLOt382pv+dlh03N1aORyRIkDReHCaAywaELRZJsTmbnyudBeYfVe+I
+DOduPqYywnWcKo58hqOw0Tnu5Pg5vyi0qo16jrxKCiy5BHmnamT8IbXmWbjc6r28
+uo4JAoGAfAPvPJ2fV5vpzr4LPoVyaSiFj414D+5XYxX6CWpdTryelpP2Rs1VfJ1a
+7EusUtWs26pAKwttDY4yoTvog7rrskgtXzisaoNMDbH/PfsoqjMnnIgakvKmHpUM
+l6E1ecWFExEg5v6yvmxFC7JIUzIYOoysWu3X44G8rQ+vDQNRFZQ=
+-----END RSA PRIVATE KEY-----

+ 24 - 0
client/rest-high-level/src/test/resources/org/elasticsearch/client/security/delegate_pki/testRootCA.crt

@@ -0,0 +1,24 @@
+-----BEGIN CERTIFICATE-----
+MIID/TCCAuWgAwIBAgIJAIAPVUXOUQDNMA0GCSqGSIb3DQEBCwUAMEsxIzAhBgNV
+BAMTGkVsYXN0aWNzZWFyY2ggVGVzdCBSb290IENBMRYwFAYDVQQLEw1lbGFzdGlj
+c2VhcmNoMQwwCgYDVQQKEwNvcmcwHhcNMTkwNzE5MTMzMjIwWhcNMjMwNzE4MTMz
+MjIwWjBLMSMwIQYDVQQDExpFbGFzdGljc2VhcmNoIFRlc3QgUm9vdCBDQTEWMBQG
+A1UECxMNZWxhc3RpY3NlYXJjaDEMMAoGA1UEChMDb3JnMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEAzIgn8r2kirt90id0uoi6YEGBPx+XDzthLbLsN+M0
+nXhj40OVcGPiww+cre14bJr0M6MG4CvFjRJc92RoVrE8+7XOKt0bgiHeVM+b0LEh
+wVMH9koararPVMo0CjCMN4ChHMOWKBPUNZswvk+pFC+QbTcfgQLycqh+lTB1O6l3
+hPnmunEqhLIj9ke3FwA326igdb+16EbKYVL2c5unNoC5ZMc5Z9bnn4/GNXptkHhy
++SvG7IZKW2pAzei3Df/n47ZhJfQKERUCe9eO7b/ZmTEzAzYj9xucE5lYcpkOZd6g
+IMU3vXe4FeD/BM4sOLkKTtMejiElEecxw8cLI9Nji/0y1wIDAQABo4HjMIHgMB0G
+A1UdDgQWBBTOEsjc6c/NooUNetM6y1FWlnyPtTAfBgNVHSMEGDAWgBTOEsjc6c/N
+ooUNetM6y1FWlnyPtTAMBgNVHRMEBTADAQH/MIGPBgNVHREEgYcwgYSCCWxvY2Fs
+aG9zdIIVbG9jYWxob3N0LmxvY2FsZG9tYWluggpsb2NhbGhvc3Q0ghdsb2NhbGhv
+c3Q0LmxvY2FsZG9tYWluNIIKbG9jYWxob3N0NoIXbG9jYWxob3N0Ni5sb2NhbGRv
+bWFpbjaHBH8AAAGHEAAAAAAAAAAAAAAAAAAAAAEwDQYJKoZIhvcNAQELBQADggEB
+ACHjwoDJILv77sQ5QN6SoAp6GYqiC9/doDIzDFCd/WP7G8EbaosHM6jM7NbrlK3g
+PNTzuY1pLPoI3YJSO4Al/UfzEffaYSbZC2QZG9F6fUSWhvR+nxzPSXWkjzIInv1j
+pPMgnUl6oJaUbsSR/evtvWNSxrM3LewkRTOoktkXM6SjTUHjdP6ikrkrarrWZgzr
+K30BqGL6kDSv9LkyXe6RSgQDtQe51Yut+lKGCcy8AoEwG/3cjb7XnrWcFsJXjYbf
+4m3QsS8yHU/O/xgyvVHOfki+uGVepzSjdzDMLE1GBkju05NR2eJZ8omj/QiJa0+z
+1d/AOKExvWvo1yQ28ORcwo4=
+-----END CERTIFICATE-----

+ 27 - 0
client/rest-high-level/src/test/resources/org/elasticsearch/client/security/delegate_pki/testRootCA.key

@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEAzIgn8r2kirt90id0uoi6YEGBPx+XDzthLbLsN+M0nXhj40OV
+cGPiww+cre14bJr0M6MG4CvFjRJc92RoVrE8+7XOKt0bgiHeVM+b0LEhwVMH9koa
+rarPVMo0CjCMN4ChHMOWKBPUNZswvk+pFC+QbTcfgQLycqh+lTB1O6l3hPnmunEq
+hLIj9ke3FwA326igdb+16EbKYVL2c5unNoC5ZMc5Z9bnn4/GNXptkHhy+SvG7IZK
+W2pAzei3Df/n47ZhJfQKERUCe9eO7b/ZmTEzAzYj9xucE5lYcpkOZd6gIMU3vXe4
+FeD/BM4sOLkKTtMejiElEecxw8cLI9Nji/0y1wIDAQABAoIBAQC6LMnoPFW1brs1
++3JWhTTZf2btlYzEcbGgjnhU2v0+xaJu8UrrFhEIq4JcE4gFm/rjsecFUPKu2eND
+0eLj3st699+lxsRObRPbMWtMyJ/IQRNDTesA4DV/odtC1zQbJXwCGcrpyjrlXNE+
+unZWiIE32PBVV+BnHBa1KHneCAFiSRLrySAiDAnTIJxB6ufweoxevLoJPPNLlbo7
+H2jv6g1Som/Imjhof4KhD/1Q04Sed2wScSS/7Bz38eO68HG4NMFY+M2/cLzrbflg
+QdeKHNhoIGnSFMEW5TCVlI4qrP8zvPPdZmLOMBT+Ocm3pc5xDAPwFYCe8wH1DVn+
+b3sVpwu5AoGBAOhFA7gUDZjRBkNAqJfbUdhdWSslePQsjeTKsu5rc4gk2aiL4bZ4
+fxG0Dq1hX7FjAmYrGqnsXsbxxDnCkhXGH1lY73kF0Zzwr2Pg1yRHyn1nCinhD4g4
+G2vBr37QtWn4wS/L7V//D3xrcCTG3QgAmvZZ99tYgqlmnUzmawdZ8kQ7AoGBAOFt
+qg7sTSNWVpKkfkyX2NXvBMt5e3Qcwnge2pX+SBgljwjNUwSSMLwxdBDSyDXIhk8W
+s4pJLtMDJsT/2WBKC9WJm9m3gc7yYZznLJ+5YPcieXHGGNXCRldPePhTIjnL591H
+CSXoc3BZ2iKK745BYuPqSuLb2XfE3/hwoaFR4S4VAoGAQ6ywG7dECu2ELJ4vQSe2
+3hq8u1SMvGAq66mfntYR8G4EORagqkDLjUXwLNY9Qnr9nPUcLLxhFQgmS0oEtHFo
+eujtxU5Lt7Vs9OXy6XA9cHJQRMl9dAwc+TWSw5ld8kV3TEzXmevAAFlxcFW82vMK
+M5MdI3zTfTYXyOst7hNoAjcCgYAhz/cgAeWYFU0q9a1UA7qsbAuGEZSo1997cPVM
+ZjWeGZQYt+Np3hudPrWwCE2rc4Zhun/3j/6L+/8GsXGDddfMkbVktJet2ME3bZ1N
+39phdzRMEnCLL3aphewZIy8RCDqhABSpMPKPuYp0f+5qofgZQ300BdHamxcVBp/X
+uJZT+QKBgQDdJQd+QxfCb8BZ11fWtyWJWQWZMmyX2EEbAIMvYQP3xh8PHmw2JoiQ
+VQ103bCkegJ1S7ubrGltdt8pyjN4rrByXJmxCe1Y/LSHIp9w8D3jaiLCRSk1EmBw
+jXjnZoiJn3GV5jmbV10hzrn7jqRcwhYA5zuoE7qb604V7cPZLzHtog==
+-----END RSA PRIVATE KEY-----

+ 1 - 1
dev-tools/smoke_test_rc.py

@@ -200,7 +200,7 @@ def smoke_test_release(release, files, hash, plugins):
       headers = {}
     print('  Starting elasticsearch deamon from [%s]' % es_dir)
     try:
-      run('%s; %s -Enode.name=smoke_tester -Ecluster.name=prepare_release -Erepositories.url.allowed_urls=http://snapshot.test* %s -Epidfile=%s -Enode.portsfile=true'
+      run('%s; %s -Enode.name=smoke_tester -Ecluster.name=prepare_release -Erepositories.url.allowed_urls=http://snapshot.test* %s -Enode.pidfile=%s -Enode.portsfile=true'
           % (java_exe(), es_run_path, '-d', os.path.join(es_dir, 'es-smoke.pid')))
       if not wait_for_node_startup(es_dir, header=headers):
         print("elasticsearch logs:")

+ 1 - 0
distribution/build.gradle

@@ -428,6 +428,7 @@ task run(type: RunTask) {
     setting 'xpack.monitoring.enabled', 'true'
     setting 'xpack.sql.enabled', 'true'
     setting 'xpack.rollup.enabled', 'true'
+    setting 'xpack.data-science.enabled', 'true'
     keystoreSetting 'bootstrap.password', 'password'
   }
 }

+ 1 - 1
distribution/docker/src/docker/bin/docker-entrypoint.sh

@@ -53,7 +53,7 @@ do
   # Elasticsearch settings need to have at least two dot separated lowercase
   # words, e.g. `cluster.name`, except for `processors` which we handle
   # specially
-  if [[ "$envvar_key" =~ ^[a-z0-9_]+\.[a-z0-9_]+ || "$envvar_key" == "processors" ]]; then
+  if [[ "$envvar_key" =~ ^[a-z0-9_]+\.[a-z0-9_]+ ]]; then
     if [[ ! -z $envvar_value ]]; then
       es_opt="-E${envvar_key}=${envvar_value}"
       es_opts+=("${es_opt}")

+ 36 - 0
docs/build.gradle

@@ -218,6 +218,42 @@ buildRestTests.setups['sales'] = '''
             {"index":{}}
             {"date": "2015/03/01 00:00:00", "price": 175, "promoted": false, "rating": 2, "type": "t-shirt"}'''
 
+// Used by cumulative cardinality aggregation docs
+buildRestTests.setups['user_hits'] = '''
+  - do:
+        indices.create:
+          index: user_hits
+          body:
+            settings:
+              number_of_shards: 1
+              number_of_replicas: 0
+            mappings:
+              properties:
+                user_id:
+                  type: keyword
+                timestamp:
+                  type: date
+  - do:
+        bulk:
+          index: user_hits
+          refresh: true
+          body: |
+            {"index":{}}
+            {"timestamp": "2019-01-01T13:00:00", "user_id": "1"}
+            {"index":{}}
+            {"timestamp": "2019-01-01T13:00:00", "user_id": "2"}
+            {"index":{}}
+            {"timestamp": "2019-01-02T13:00:00", "user_id": "1"}
+            {"index":{}}
+            {"timestamp": "2019-01-02T13:00:00", "user_id": "3"}
+            {"index":{}}
+            {"timestamp": "2019-01-03T13:00:00", "user_id": "1"}
+            {"index":{}}
+            {"timestamp": "2019-01-03T13:00:00", "user_id": "2"}
+            {"index":{}}
+            {"timestamp": "2019-01-03T13:00:00", "user_id": "4"}'''
+
+
 // Dummy bank account data used by getting-started.asciidoc
 buildRestTests.setups['bank'] = '''
   - do:

+ 24 - 4
docs/java-rest/high-level/ml/put-data-frame-analytics.asciidoc

@@ -75,25 +75,45 @@ include-tagged::{doc-tests-file}[{api}-dest-config]
 ==== Analysis
 
 The analysis to be performed.
-Currently, only one analysis is supported: +OutlierDetection+.
+Currently, the supported analyses include : +OutlierDetection+, +Regression+.
+
+===== Outlier Detection
 
 +OutlierDetection+ analysis can be created in one of two ways:
 
 ["source","java",subs="attributes,callouts,macros"]
 --------------------------------------------------
-include-tagged::{doc-tests-file}[{api}-analysis-default]
+include-tagged::{doc-tests-file}[{api}-outlier-detection-default]
 --------------------------------------------------
 <1> Constructing a new OutlierDetection object with default strategy to determine outliers
 
 or
 ["source","java",subs="attributes,callouts,macros"]
 --------------------------------------------------
-include-tagged::{doc-tests-file}[{api}-analysis-customized]
+include-tagged::{doc-tests-file}[{api}-outlier-detection-customized]
 --------------------------------------------------
 <1> Constructing a new OutlierDetection object
 <2> The method used to perform the analysis
 <3> Number of neighbors taken into account during analysis
 
+===== Regression
+
++Regression+ analysis requires to set which is the +dependent_variable+ and
+has a number of other optional parameters:
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests-file}[{api}-regression]
+--------------------------------------------------
+<1> Constructing a new Regression builder object with the required dependent variable
+<2> The lambda regularization parameter. A non-negative double.
+<3> The gamma regularization parameter. A non-negative double.
+<4> The applied shrinkage. A double in [0.001, 1].
+<5> The maximum number of trees the forest is allowed to contain. An integer in [1, 2000].
+<6> The fraction of features which will be used when selecting a random bag for each candidate split. A double in (0, 1].
+<7> The name of the prediction field in the results object.
+<8> The percentage of training-eligible rows to be used in training. Defaults to 100%.
+
 ==== Analyzed fields
 
 FetchContext object containing fields to be included in / excluded from the analysis
@@ -113,4 +133,4 @@ The returned +{response}+ contains the newly created {dataframe-analytics-config
 ["source","java",subs="attributes,callouts,macros"]
 --------------------------------------------------
 include-tagged::{doc-tests-file}[{api}-response]
---------------------------------------------------
+--------------------------------------------------

+ 62 - 0
docs/java-rest/high-level/security/delegate-pki-authentication.asciidoc

@@ -0,0 +1,62 @@
+--
+:api: delegate-pki
+:request: DelegatePkiAuthenticationRequest
+:response: DelegatePkiAuthenticationResponse
+--
+
+[id="{upid}-{api}"]
+=== Delegate PKI Authentication API
+
+This API is called by *smart* proxies to Elasticsearch, such as Kibana, that
+terminate the user's TLS session but that still wish to authenticate the user
+on the Elasticsearch side using a PKI realm, which normally requires users to
+authenticate over TLS directly to Elasticsearch. It implements the exchange of
+the client's {@code X509Certificate} chain from the TLS authentication into an
+Elasticsearch access token.
+
+IMPORTANT: The association between the subject public key in the target
+certificate and the corresponding private key is *not* validated. This is part
+of the TLS authentication process and it is delegated to the proxy calling this
+API. The proxy is *trusted* to have performed the TLS authentication, and this
+API translates that authentication into an Elasticsearch access token.
+
+[id="{upid}-{api}-request"]
+==== Delegate PKI Authentication Request
+
+The request contains the client's {@code X509Certificate} chain. The
+certificate chain is represented as a list where the first element is the
+target certificate containing the subject distinguished name that is requesting
+access. This may be followed by additional certificates, with each subsequent
+certificate being the one used to certify the previous one. The certificate
+chain is validated according to RFC 5280, by sequentially considering the trust
+configuration of every installed {@code PkiRealm} that has {@code
+PkiRealmSettings#DELEGATION_ENABLED_SETTING} set to {@code true} (default is
+{@code false}). A successfully trusted target certificate is also subject to
+the validation of the subject distinguished name according to that respective's
+realm {@code PkiRealmSettings#USERNAME_PATTERN_SETTING}.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SecurityDocumentationIT.java[delegate-pki-request]
+--------------------------------------------------
+
+include::../execution.asciidoc[]
+
+[id="{upid}-{api}-response"]
+==== Delegate PKI Authentication Response
+
+The returned +{response}+ contains the following properties:
+
+`accessToken`:: This is the newly created access token.
+   It can be used to authenticate to the Elasticsearch cluster.
+`type`:: The type of the token, this is always `"Bearer"`.
+`expiresIn`:: The length of time (in seconds) until the token will expire.
+   The token will be considered invalid after that time.
+
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests}/SecurityDocumentationIT.java[delegate-pki-response]
+--------------------------------------------------
+<1> The `accessToken` can be used to authentication to Elasticsearch.
+
+

+ 8 - 0
docs/java-rest/high-level/security/get-api-key.asciidoc

@@ -21,6 +21,8 @@ The +{request}+ supports retrieving API key information for
 
 . All API keys for a specific user in a specific realm
 
+. A specific key or all API keys owned by the current authenticated user
+
 ===== Retrieve a specific API key by its id
 ["source","java",subs="attributes,callouts,macros"]
 --------------------------------------------------
@@ -51,6 +53,12 @@ include-tagged::{doc-tests-file}[get-user-api-keys-request]
 include-tagged::{doc-tests-file}[get-user-realm-api-keys-request]
 --------------------------------------------------
 
+===== Retrieve all API keys for the current authenticated user
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests-file}[get-api-keys-owned-by-authenticated-user-request]
+--------------------------------------------------
+
 include::../execution.asciidoc[]
 
 [id="{upid}-{api}-response"]

+ 8 - 0
docs/java-rest/high-level/security/invalidate-api-key.asciidoc

@@ -21,6 +21,8 @@ The +{request}+ supports invalidating
 
 . All API keys for a specific user in a specific realm
 
+. A specific key or all API keys owned by the current authenticated user
+
 ===== Specific API key by API key id
 ["source","java",subs="attributes,callouts,macros"]
 --------------------------------------------------
@@ -51,6 +53,12 @@ include-tagged::{doc-tests-file}[invalidate-user-api-keys-request]
 include-tagged::{doc-tests-file}[invalidate-user-realm-api-keys-request]
 --------------------------------------------------
 
+===== Retrieve all API keys for the current authenticated user
+["source","java",subs="attributes,callouts,macros"]
+--------------------------------------------------
+include-tagged::{doc-tests-file}[invalidate-api-keys-owned-by-authenticated-user-request]
+--------------------------------------------------
+
 include::../execution.asciidoc[]
 
 [id="{upid}-{api}-response"]

+ 235 - 0
docs/reference/aggregations/pipeline/cumulative-cardinality-aggregation.asciidoc

@@ -0,0 +1,235 @@
+[role="xpack"]
+[testenv="basic"]
+[[search-aggregations-pipeline-cumulative-cardinality-aggregation]]
+=== Cumulative Cardinality Aggregation
+
+A parent pipeline aggregation which calculates the Cumulative Cardinality in a parent histogram (or date_histogram)
+aggregation. The specified metric must be a cardinality aggregation and the enclosing histogram 
+must have `min_doc_count` set to `0` (default for `histogram` aggregations).
+
+The `cumulative_cardinality` agg is useful for finding "total new items", like the number of new visitors to your
+website each day.  A regular cardinality aggregation will tell you how many unique visitors came each day, but doesn't
+differentiate between "new" or "repeat" visitors.  The Cumulative Cardinality aggregation can be used to determine
+how many of each day's unique visitors are "new".
+
+==== Syntax
+
+A `cumulative_cardinality` aggregation looks like this in isolation:
+
+[source,js]
+--------------------------------------------------
+{
+    "cumulative_cardinality": {
+        "buckets_path": "my_cardinality_agg"
+    }
+}
+--------------------------------------------------
+// NOTCONSOLE
+
+[[cumulative-cardinality-params]]
+.`cumulative_cardinality` Parameters
+[options="header"]
+|===
+|Parameter Name |Description |Required |Default Value
+|`buckets_path` |The path to the cardinality aggregation we wish to find the cumulative cardinality for (see <<buckets-path-syntax>> for more
+ details) |Required |
+|`format` |format to apply to the output value of this aggregation |Optional |`null` 
+|===
+
+The following snippet calculates the cumulative cardinality of the total daily `users`:
+
+[source,js]
+--------------------------------------------------
+GET /user_hits/_search
+{
+    "size": 0,
+    "aggs" : {
+        "users_per_day" : {
+            "date_histogram" : {
+                "field" : "timestamp",
+                "calendar_interval" : "day"
+            },
+            "aggs": {
+                "distinct_users": {
+                    "cardinality": {
+                        "field": "user_id"
+                    }
+                },
+                "total_new_users": {
+                    "cumulative_cardinality": {
+                        "buckets_path": "distinct_users" <1>
+                    }
+                }
+            }
+        }
+    }
+}
+--------------------------------------------------
+// CONSOLE
+// TEST[setup:user_hits]
+
+<1> `buckets_path` instructs this aggregation to use the output of the `distinct_users` aggregation for the cumulative cardinality
+
+And the following may be the response:
+
+[source,js]
+--------------------------------------------------
+{
+   "took": 11,
+   "timed_out": false,
+   "_shards": ...,
+   "hits": ...,
+   "aggregations": {
+      "users_per_day": {
+         "buckets": [
+            {
+               "key_as_string": "2019-01-01T00:00:00.000Z",
+               "key": 1546300800000,
+               "doc_count": 2,
+               "distinct_users": {
+                  "value": 2
+               },
+               "total_new_users": {
+                  "value": 2
+               }
+            },
+            {
+               "key_as_string": "2019-01-02T00:00:00.000Z",
+               "key": 1546387200000,
+               "doc_count": 2,
+               "distinct_users": {
+                  "value": 2
+               },
+               "total_new_users": {
+                  "value": 3
+               }
+            },
+            {
+               "key_as_string": "2019-01-03T00:00:00.000Z",
+               "key": 1546473600000,
+               "doc_count": 3,
+               "distinct_users": {
+                  "value": 3
+               },
+               "total_new_users": {
+                  "value": 4
+               }
+            }
+         ]
+      }
+   }
+}
+--------------------------------------------------
+// TESTRESPONSE[s/"took": 11/"took": $body.took/]
+// TESTRESPONSE[s/"_shards": \.\.\./"_shards": $body._shards/]
+// TESTRESPONSE[s/"hits": \.\.\./"hits": $body.hits/]
+
+
+Note how the second day, `2019-01-02`, has two distinct users but the `total_new_users` metric generated by the
+cumulative pipeline agg only increments to three.  This means that only one of the two users that day were
+new, the other had already been seen in the previous day.  This happens again on the third day, where only
+one of three users is completely new.
+
+==== Incremental cumulative cardinality
+
+The `cumulative_cardinality` agg will show you the total, distinct count since the beginning of the time period
+being queried.  Sometimes, however, it is useful to see the "incremental" count.  Meaning, how many new users
+are added each day, rather than the total cumulative count.
+
+This can be accomplished by adding a `derivative` aggregation to our query:
+
+[source,js]
+--------------------------------------------------
+GET /user_hits/_search
+{
+    "size": 0,
+    "aggs" : {
+        "users_per_day" : {
+            "date_histogram" : {
+                "field" : "timestamp",
+                "calendar_interval" : "day"
+            },
+            "aggs": {
+                "distinct_users": {
+                    "cardinality": {
+                        "field": "user_id"
+                    }
+                },
+                "total_new_users": {
+                    "cumulative_cardinality": {
+                        "buckets_path": "distinct_users"
+                    }
+                },
+                "incremental_new_users": {
+                    "derivative": {
+                        "buckets_path": "total_new_users"
+                    }
+                }
+            }
+        }
+    }
+}
+--------------------------------------------------
+// CONSOLE
+// TEST[setup:user_hits]
+
+
+And the following may be the response:
+
+[source,js]
+--------------------------------------------------
+{
+   "took": 11,
+   "timed_out": false,
+   "_shards": ...,
+   "hits": ...,
+   "aggregations": {
+      "users_per_day": {
+         "buckets": [
+            {
+               "key_as_string": "2019-01-01T00:00:00.000Z",
+               "key": 1546300800000,
+               "doc_count": 2,
+               "distinct_users": {
+                  "value": 2
+               },
+               "total_new_users": {
+                  "value": 2
+               }
+            },
+            {
+               "key_as_string": "2019-01-02T00:00:00.000Z",
+               "key": 1546387200000,
+               "doc_count": 2,
+               "distinct_users": {
+                  "value": 2
+               },
+               "total_new_users": {
+                  "value": 3
+               },
+               "incremental_new_users": {
+                  "value": 1.0
+               }
+            },
+            {
+               "key_as_string": "2019-01-03T00:00:00.000Z",
+               "key": 1546473600000,
+               "doc_count": 3,
+               "distinct_users": {
+                  "value": 3
+               },
+               "total_new_users": {
+                  "value": 4
+               },
+               "incremental_new_users": {
+                  "value": 1.0
+               }
+            }
+         ]
+      }
+   }
+}
+--------------------------------------------------
+// TESTRESPONSE[s/"took": 11/"took": $body.took/]
+// TESTRESPONSE[s/"_shards": \.\.\./"_shards": $body._shards/]
+// TESTRESPONSE[s/"hits": \.\.\./"hits": $body.hits/]

+ 1 - 1
docs/reference/cat/alias.asciidoc

@@ -11,7 +11,7 @@ filter and routing information.
 [[cat-alias-api-request]]
 ==== {api-request-title}
 
-`GET /_cat/aliases/{name}`
+`GET /_cat/aliases/<name>`
 
 [[cat-alias-api-path-params]]
 ==== {api-path-parms-title}

+ 1 - 1
docs/reference/cat/allocation.asciidoc

@@ -12,7 +12,7 @@ and their disk space.
 [[cat-allocation-api-request]]
 ==== {api-request-title}
 
-`GET /_cat/allocation/{node_id}`
+`GET /_cat/allocation/<node_id>`
 
 [[cat-allocation-api-path-params]]
 ==== {api-path-parms-title}

+ 1 - 1
docs/reference/cat/count.asciidoc

@@ -14,7 +14,7 @@ which have not yet been removed by the merge process.
 [[cat-count-api-request]]
 ==== {api-request-title}
 
-`GET /_cat/count/{index}`
+`GET /_cat/count/<index>`
 
 
 [[cat-count-api-path-params]]

+ 2 - 2
docs/reference/cat/fielddata.asciidoc

@@ -11,13 +11,13 @@ in the cluster.
 [[cat-fielddata-api-request]]
 ==== {api-request-title}
 
-`GET /_cat/fielddata/{fields}`
+`GET /_cat/fielddata/<field>`
 
 
 [[cat-fielddata-api-path-params]]
 ==== {api-path-parms-title}
 
-`{fields}`::
+`<field>`::
 (Optional, string) Comma-separated list of fields used to limit returned
 information.
 

+ 1 - 1
docs/reference/cat/indices.asciidoc

@@ -10,7 +10,7 @@ Returns high-level information about indices in a cluster.
 [[cat-indices-api-request]]
 ==== {api-request-title}
 
-`GET /_cat/indices/{index}`
+`GET /_cat/indices/<index>`
 
 
 [[cat-indices-api-desc]]

+ 1 - 1
docs/reference/cat/recovery.asciidoc

@@ -11,7 +11,7 @@ to the <<indices-recovery, indices recovery>> API.
 [[cat-recovery-api-request]]
 ==== {api-request-title}
 
-`GET /_cat/recovery/{index}`
+`GET /_cat/recovery/<index>`
 
 
 [[cat-recovery-api-desc]]

+ 1 - 1
docs/reference/cat/segments.asciidoc

@@ -11,7 +11,7 @@ API.
 [[cat-segments-api-request]]
 ==== {api-request-title}
 
-`GET /_cat/segments/{index}`
+`GET /_cat/segments/<index>`
 
 
 [[cat-segments-path-params]]

+ 1 - 1
docs/reference/cat/shards.asciidoc

@@ -12,7 +12,7 @@ docs, the bytes it takes on disk, and the node where it's located.
 [[cat-shards-api-request]]
 ==== {api-request-title}
 
-`GET /_cat/shards/{index}`
+`GET /_cat/shards/<index>`
 
 
 [[cat-shards-path-params]]

+ 2 - 2
docs/reference/cat/snapshots.asciidoc

@@ -11,13 +11,13 @@ more repositories. A snapshot is a backup of an index or running {es} cluster.
 [[cat-snapshots-api-request]]
 ==== {api-request-title}
 
-`GET /_cat/snapshots/{repository}`
+`GET /_cat/snapshots/<repository>`
 
 
 [[cat-snapshots-path-params]]
 ==== {api-path-parms-title}
 
-`{repository}`::
+`<repository>`::
 +
 --
 (Optional, string) Comma-separated list of snapshot repositories used to limit

+ 2 - 2
docs/reference/cat/templates.asciidoc

@@ -12,13 +12,13 @@ and <<mapping,field mappings>> to new indices at creation.
 [[cat-templates-api-request]]
 ==== {api-request-title}
 
-`GET /_cat/templates/{template_name}`
+`GET /_cat/templates/<template_name>`
 
 
 [[cat-templates-path-params]]
 ==== {api-path-parms-title}
 
-`{template_name}`::
+`<template_name>`::
 (Optional, string) Comma-separated list of index template names used to limit
 the request. Accepts wildcard expressions.
 

+ 2 - 2
docs/reference/cat/thread_pool.asciidoc

@@ -12,12 +12,12 @@ pools.
 [[cat-thread-pool-api-request]]
 ==== {api-request-title}
 
-`GET /_cat/thread_pool/{thread_pool}`
+`GET /_cat/thread_pool/<thread_pool>`
 
 [[cat-thread-pool-path-params]]
 ==== {api-path-parms-title}
 
-`{thread_pool}`::
+`<thread_pool>`::
 (Optional, string) Comma-separated list of thread pool names used to limit the
 request. Accepts wildcard expressions.
 

+ 1 - 1
docs/reference/cluster/health.asciidoc

@@ -6,7 +6,7 @@ Returns the health status of a cluster.
 [[cluster-health-api-request]]
 ==== {api-request-title}
 
-`GET _cluster/health/{index}`
+`GET _cluster/health/<index>`
 
 [[cluster-health-api-desc]]
 ==== {api-description-title}

+ 1 - 1
docs/reference/cluster/nodes-hot-threads.asciidoc

@@ -9,7 +9,7 @@ Returns the hot threads on each selected node in the cluster.
 
 `GET /_nodes/hot_threads` +
 
-`GET /_nodes/{node_id}/hot_threads`
+`GET /_nodes/<node_id>/hot_threads`
 
 
 [[cluster-nodes-hot-threads-api-desc]]

+ 4 - 4
docs/reference/cluster/nodes-info.asciidoc

@@ -9,11 +9,11 @@ Returns cluster nodes information.
 
 `GET /_nodes` +
 
-`GET /_nodes/{node_id}` +
+`GET /_nodes/<node_id>` +
 
-`GET /_nodes/{metric}` +
+`GET /_nodes/<metric>` +
 
-`GET /_nodes/{node_id}/{metric}`
+`GET /_nodes/<node_id>/<metric>`
 
 
 [[cluster-nodes-info-api-desc]]
@@ -29,7 +29,7 @@ By default, it returns all attributes and core settings for a node.
 [[cluster-nodes-info-api-path-params]]
 ==== {api-path-parms-title}
 
-`{metric}`::
+`<metric>`::
 		(Optional, string) Limits the information returned to the specific metrics. 
 		A comma-separated list of the following options:
 +

+ 7 - 7
docs/reference/cluster/nodes-stats.asciidoc

@@ -9,15 +9,15 @@ Returns cluster nodes statistics.
 
 `GET /_nodes/stats` +
 
-`GET /_nodes/{node_id}/stats` +
+`GET /_nodes/<node_id>/stats` +
 
-`GET/_nodes/stats/{metric}` +
+`GET/_nodes/stats/<metric>` +
 
-`GET/_nodes/{node_id}/stats/{metric}` +
+`GET/_nodes/<node_id>/stats/<metric>` +
 
-`GET /_nodes/stats/{metric}/{index_metric}` +
+`GET /_nodes/stats/<metric>/<index_metric>` +
 
-`GET /_nodes/{node_id}/stats/{metric}/{index_metric}`
+`GET /_nodes/<node_id>/stats/<metric>/<index_metric>`
 
 
 [[cluster-nodes-stats-api-desc]]
@@ -35,7 +35,7 @@ using metrics.
 ==== {api-path-parms-title}
 
 
-`{metric}`::
+`<metric>`::
     (Optional, string) Limits the information returned to the specific metrics. 
     A comma-separated list of the following options: 
 +
@@ -83,7 +83,7 @@ using metrics.
       communication.
 --
 
-`{index_metric}`::
+`<index_metric>`::
     (Optional, string) Limit the information returned for `indices` metric to 
     the specific index metrics. It can be used only if `indices` (or `all`) 
     metric is specified. Supported metrics are:

+ 4 - 4
docs/reference/cluster/nodes-usage.asciidoc

@@ -9,11 +9,11 @@ Returns information on the usage of features.
 
 `GET /_nodes/usage` +
 
-`GET /_nodes/{node_id}/usage` +
+`GET /_nodes/<node_id>/usage` +
 
-`GET /_nodes/usage/{metric}` +
+`GET /_nodes/usage/<metric>` +
 
-`GET /_nodes/{node_id}/usage/{metric}`
+`GET /_nodes/<node_id>/usage/<metric>`
 
 
 [[cluster-nodes-usage-api-desc]]
@@ -27,7 +27,7 @@ of features for each node. All the nodes selective options are explained
 [[cluster-nodes-usage-api-path-params]]
 ==== {api-path-parms-title}
 
-`{metric}`::
+`<metric>`::
     (Optional, string) Limits the information returned to the specific metrics. 
     A comma-separated list of the following options: 
 +

+ 2 - 2
docs/reference/cluster/state.asciidoc

@@ -6,7 +6,7 @@ Returns metadata about the state of the cluster.
 [[cluster-state-api-request]]
 ==== {api-request-title}
 
-`GET /_cluster/state/{metrics}/{index}`
+`GET /_cluster/state/<metrics>/<index>`
 
 [[cluster-state-api-desc]]
 ==== {api-description-title}
@@ -50,7 +50,7 @@ including their mappings, as well as templates and other metadata. This means it
 can sometimes be quite large. To avoid the need to process all this information
 you can request only the part of the cluster state that you need:
 
-`{metrics}`::
+`<metrics>`::
     (Optional, string) A comma-separated list of the following options:
 +
 --

+ 1 - 1
docs/reference/cluster/stats.asciidoc

@@ -9,7 +9,7 @@ Returns cluster statistics.
 
 `GET /_cluster/stats` +
 
-`GET /_cluster/stats/nodes/{node_id}`
+`GET /_cluster/stats/nodes/<node_id>`
 
 
 [[cluster-stats-api-desc]]

+ 2 - 2
docs/reference/cluster/tasks.asciidoc

@@ -10,7 +10,7 @@ Returns information about the tasks currently executing in the cluster.
 
 `GET /_tasks` +
 
-`GET /_tasks/{task_id}`
+`GET /_tasks/<task_id>`
 
 
 [[tasks-api-desc]]
@@ -23,7 +23,7 @@ executing on one or more nodes in the cluster.
 [[tasks-api-path-params]]
 ==== {api-path-parms-title}
 
-{task_id}
+`<task_id>`::
     (Optional, string) The ID of the task to return (`node_id:task_number`).
 
 

+ 2 - 2
docs/reference/cluster/voting-exclusions.asciidoc

@@ -11,7 +11,7 @@ Adds or removes master-eligible nodes from the
 [[voting-config-exclusions-api-request]]
 ==== {api-request-title}
 
-`POST _cluster/voting_config_exclusions/{node_name}` +
+`POST _cluster/voting_config_exclusions/<node_name>` +
 
 `DELETE _cluster/voting_config_exclusions`
 
@@ -46,7 +46,7 @@ For more information, see <<modules-discovery-removing-nodes>>.
 [[voting-config-exclusions-api-path-params]]
 ==== {api-path-parms-title}
 
-`{node_name}`::
+`<node_name>`::
   A <<cluster-nodes,node filter>> that identifies {es} nodes.
 
 

+ 3 - 3
docs/reference/docs/get.asciidoc

@@ -240,7 +240,7 @@ GET twitter/_doc/0
 
 The API returns the following result:
 
-[source,js]
+[source,console-result]
 --------------------------------------------------
 {
     "_index" : "twitter",
@@ -359,7 +359,7 @@ GET twitter/_doc/1?stored_fields=tags,counter
 
 The API returns the following result:
 
-[source,js]
+[source,console-result]
 --------------------------------------------------
 {
    "_index": "twitter",
@@ -403,7 +403,7 @@ GET twitter/_doc/2?routing=user1&stored_fields=tags,counter
 
 The API returns the following result:
 
-[source,js]
+[source,console-result]
 --------------------------------------------------
 {
    "_index": "twitter",

+ 106 - 340
docs/reference/getting-started.asciidoc

@@ -17,7 +17,7 @@ Follow this getting started tutorial to:
 Need more context?
 
 Check out the <<elasticsearch-intro,
-Elasticsearch Introduction>> to learn the lingo and understand the basics of
+{es} Introduction>> to learn the lingo and understand the basics of
 how {es} works. If you're already familiar with {es} and want to see how it works
 with the rest of the stack, you might want to jump to the
 {stack-gs}/get-started-elastic-stack.html[Elastic Stack
@@ -26,29 +26,45 @@ Tutorial] to see how to set up a system monitoring solution with {es}, {kib},
 
 TIP: The fastest way to get started with {es} is to
 https://www.elastic.co/cloud/elasticsearch-service/signup[start a free 14-day
-trial of Elasticsearch Service] in the cloud.
+trial of {ess}] in the cloud.
 --
 
 [[getting-started-install]]
 == Get {es} up and running
 
-To take {es} for a test drive, you can create a one-click cloud deployment
-on the https://www.elastic.co/cloud/elasticsearch-service/signup[Elasticsearch Service],
-or <<run-elasticsearch-local, set up a multi-node {es} cluster>> on your own
+To take {es} for a test drive, you can create a 
+https://www.elastic.co/cloud/elasticsearch-service/signup[hosted deployment]  on 
+the {ess} or set up a multi-node {es} cluster on your own
 Linux, macOS, or Windows machine.
 
+[float]
+[[run-elasticsearch-hosted]]
+=== Run {es} on Elastic Cloud
+
+When you create a deployment on the {es} Service, the service provisions
+a three-node {es} cluster along with Kibana and APM.
+
+To create a deployment:
+
+. Sign up for a https://www.elastic.co/cloud/elasticsearch-service/signup[free trial] 
+and verify your email address.
+. Set a password for your account.
+. Click **Create Deployment**.
+
+Once you've created a deployment, you're ready to <<getting-started-index>>.
 
 [float]
 [[run-elasticsearch-local]]
 === Run {es} locally on Linux, macOS, or Windows
 
-When you create a cluster on the Elasticsearch Service, you automatically
-get a three-node cluster. By installing from the tar or zip archive, you can
-start multiple instances of {es} locally to see how a multi-node cluster behaves.
+When you create a deployment on the {ess}, a master node and
+two data nodes are provisioned automatically. By installing from the tar or zip 
+archive, you can start multiple instances of {es} locally to see how a multi-node 
+cluster behaves.
 
 To run a three-node {es} cluster locally:
 
-. Download the Elasticsearch archive for your OS:
+. Download the {es} archive for your OS:
 +
 Linux: https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-{version}-linux-x86_64.tar.gz[elasticsearch-{version}-linux-x86_64.tar.gz]
 +
@@ -92,7 +108,7 @@ Windows PowerShell:
 Expand-Archive elasticsearch-{version}-windows-x86_64.zip
 --------------------------------------------------
 
-. Start elasticsearch from the `bin` directory:
+. Start {es} from the `bin` directory:
 +
 Linux and macOS:
 +
@@ -332,82 +348,14 @@ yellow open   bank  l7sSYV2cQXmu6_4rJWVIww   5   1       1000            0    12
 [[getting-started-search]]
 == Start searching
 
-Now let's start with some simple searches. There are two basic ways to run searches: one is by sending search parameters through the {ref}/search-uri-request.html[REST request URI] and the other by sending them through the {ref}/search-request-body.html[REST request body]. The request body method allows you to be more expressive and also to define your searches in a more readable JSON format. We'll try one example of the request URI method but for the remainder of this tutorial, we will exclusively be using the request body method.
-
-The REST API for search is accessible from the `_search` endpoint. This example returns all documents in the bank index:
-
-[source,js]
---------------------------------------------------
-GET /bank/_search?q=*&sort=account_number:asc&pretty
---------------------------------------------------
-// CONSOLE
-// TEST[continued]
-
-Let's first dissect the search call. We are searching (`_search` endpoint) in the bank index, and the `q=*` parameter instructs Elasticsearch to match all documents in the index. The `sort=account_number:asc` parameter indicates to sort the results using the `account_number` field of each document in an ascending order. The `pretty` parameter, again, just tells Elasticsearch to return pretty-printed JSON results.
-
-And the response (partially shown):
-
-[source,js]
---------------------------------------------------
-{
-  "took" : 63,
-  "timed_out" : false,
-  "_shards" : {
-    "total" : 5,
-    "successful" : 5,
-    "skipped" : 0,
-    "failed" : 0
-  },
-  "hits" : {
-    "total" : {
-        "value": 1000,
-        "relation": "eq"
-    },
-    "max_score" : null,
-    "hits" : [ {
-      "_index" : "bank",
-      "_type" : "_doc",
-      "_id" : "0",
-      "sort": [0],
-      "_score" : null,
-      "_source" : {"account_number":0,"balance":16623,"firstname":"Bradshaw","lastname":"Mckenzie","age":29,"gender":"F","address":"244 Columbus Place","employer":"Euron","email":"bradshawmckenzie@euron.com","city":"Hobucken","state":"CO"}
-    }, {
-      "_index" : "bank",
-      "_type" : "_doc",
-      "_id" : "1",
-      "sort": [1],
-      "_score" : null,
-      "_source" : {"account_number":1,"balance":39225,"firstname":"Amber","lastname":"Duke","age":32,"gender":"M","address":"880 Holmes Lane","employer":"Pyrami","email":"amberduke@pyrami.com","city":"Brogan","state":"IL"}
-    }, ...
-    ]
-  }
-}
---------------------------------------------------
-// TESTRESPONSE[s/"took" : 63/"took" : $body.took/]
-// TESTRESPONSE[s/\.\.\./$body.hits.hits.2, $body.hits.hits.3, $body.hits.hits.4, $body.hits.hits.5, $body.hits.hits.6, $body.hits.hits.7, $body.hits.hits.8, $body.hits.hits.9/]
+Once you have ingested some data into an {es} index, you can search it
+by sending requests to the `_search` endpoint. To access the full suite of
+search capabilities, you use the {es} Query DSL to specify the
+search criteria in the request body. You specify the name of the index you 
+want to search in the request URI.
 
-As for the response, we see the following parts:
-
-* `took` – time in milliseconds for Elasticsearch to execute the search
-* `timed_out` – tells us if the search timed out or not
-* `_shards` – tells us how many shards were searched, as well as a count of the successful/failed searched shards
-* `hits` – search results
-* `hits.total` – an object that contains information about the total number of documents matching our search criteria
-** `hits.total.value` - the value of the total hit count (must be interpreted in the context of `hits.total.relation`).
-** `hits.total.relation` - whether `hits.total.value` is the exact hit count, in which case it is equal to `"eq"` or a
-                           lower bound of the total hit count (greater than or equals), in which case it is equal to `gte`.
-* `hits.hits` – actual array of search results (defaults to first 10 documents)
-* `hits.sort` - sort value of the sort key for each result (missing if sorting by score)
-* `hits._score` and `max_score` - ignore these fields for now
-
-The accuracy of `hits.total` is controlled by the request parameter `track_total_hits`, when set to true
-the request will track the total hits accurately (`"relation": "eq"`). It defaults to `10,000`
-which means that the total hit count is accurately tracked up to `10,000` documents.
-You can force an accurate count by setting `track_total_hits` to true explicitly.
-See the <<request-body-search-track-total-hits, request body>> documentation
-for more details.
-
-Here is the same exact search above using the alternative request body method:
+For example, the following request retrieves all documents in the `bank`
+index sorted by account number:
 
 [source,js]
 --------------------------------------------------
@@ -422,11 +370,8 @@ GET /bank/_search
 // CONSOLE
 // TEST[continued]
 
-The difference here is that instead of passing `q=*` in the URI, we provide a JSON-style query request body to the `_search` API. We'll discuss this JSON query in the next section.
-
-////
-Hidden response just so we can assert that it is indeed the same but don't have
-to clutter the docs with it:
+By default, the `hits` section of the response includes the first 10 documents
+that match the search criteria:
 
 [source,js]
 --------------------------------------------------
@@ -441,23 +386,23 @@ to clutter the docs with it:
   },
   "hits" : {
     "total" : {
-       "value": 1000,
-       "relation": "eq"
+        "value": 1000,
+        "relation": "eq"
     },
-    "max_score": null,
+    "max_score" : null,
     "hits" : [ {
       "_index" : "bank",
       "_type" : "_doc",
       "_id" : "0",
       "sort": [0],
-      "_score": null,
+      "_score" : null,
       "_source" : {"account_number":0,"balance":16623,"firstname":"Bradshaw","lastname":"Mckenzie","age":29,"gender":"F","address":"244 Columbus Place","employer":"Euron","email":"bradshawmckenzie@euron.com","city":"Hobucken","state":"CO"}
     }, {
       "_index" : "bank",
       "_type" : "_doc",
       "_id" : "1",
       "sort": [1],
-      "_score": null,
+      "_score" : null,
       "_source" : {"account_number":1,"balance":39225,"firstname":"Amber","lastname":"Duke","age":32,"gender":"M","address":"880 Holmes Lane","employer":"Pyrami","email":"amberduke@pyrami.com","city":"Brogan","state":"IL"}
     }, ...
     ]
@@ -467,54 +412,31 @@ to clutter the docs with it:
 // TESTRESPONSE[s/"took" : 63/"took" : $body.took/]
 // TESTRESPONSE[s/\.\.\./$body.hits.hits.2, $body.hits.hits.3, $body.hits.hits.4, $body.hits.hits.5, $body.hits.hits.6, $body.hits.hits.7, $body.hits.hits.8, $body.hits.hits.9/]
 
-////
-
-It is important to understand that once you get your search results back, Elasticsearch is completely done with the request and does not maintain any kind of server-side resources or open cursors into your results. This is in stark contrast to many other platforms such as SQL wherein you may initially get a partial subset of your query results up-front and then you have to continuously go back to the server if you want to fetch (or page through) the rest of the results using some kind of stateful server-side cursor.
-
-[float]
-[[getting-started-query-lang]]
-=== Introducing the Query Language
-
-Elasticsearch provides a JSON-style domain-specific language that you can use to execute queries. This is referred to as the {ref}/query-dsl.html[Query DSL]. The query language is quite comprehensive and can be intimidating at first glance but the best way to actually learn it is to start with a few basic examples.
+The response also provides the following information about the search request:
 
-Going back to our last example, we executed this query:
-
-[source,js]
---------------------------------------------------
-GET /bank/_search
-{
-  "query": { "match_all": {} }
-}
---------------------------------------------------
-// CONSOLE
-// TEST[continued]
-
-Dissecting the above, the `query` part tells us what our query definition is and the `match_all` part is simply the type of query that we want to run. The `match_all` query is simply a search for all documents in the specified index.
-
-In addition to the `query` parameter, we also can pass other parameters to
-influence the search results. In the example in the section above we passed in
-`sort`, here we pass in `size`:
-
-[source,js]
---------------------------------------------------
-GET /bank/_search
-{
-  "query": { "match_all": {} },
-  "size": 1
-}
---------------------------------------------------
-// CONSOLE
-// TEST[continued]
+* `took` – how long it took {es} to run the query, in milliseconds
+* `timed_out` – whether or not the search request timed out
+* `_shards` – how many shards were searched and a breakdown of how many shards
+succeeded, failed, or were skipped. 
+* `max_score` – the score of the most relevant document found
+* `hits.total.value` - how many matching documents were found
+* `hits.sort` - the document's sort position (when not sorting by relevance score)
+* `hits._score` - the document's relevance score (not applicable when using `match_all`)
 
-Note that if `size` is not specified, it defaults to 10.
+Each search request is self-contained: {es} does not maintain any
+state information across requests. To page through the search hits, specify
+the `from` and `size` parameters in your request. 
 
-This example does a `match_all` and returns documents 10 through 19:
+For example, the following request gets hits 10 through 19:
 
 [source,js]
 --------------------------------------------------
 GET /bank/_search
 {
   "query": { "match_all": {} },
+  "sort": [
+    { "account_number": "asc" }
+  ],
   "from": 10,
   "size": 10
 }
@@ -522,67 +444,12 @@ GET /bank/_search
 // CONSOLE
 // TEST[continued]
 
-The `from` parameter (0-based) specifies which document index to start from and the `size` parameter specifies how many documents to return starting at the from parameter. This feature is useful when implementing paging of search results. Note that if `from` is not specified, it defaults to 0.
-
-This example does a `match_all` and sorts the results by account balance in descending order and returns the top 10 (default size) documents.
-
-[source,js]
---------------------------------------------------
-GET /bank/_search
-{
-  "query": { "match_all": {} },
-  "sort": { "balance": { "order": "desc" } }
-}
---------------------------------------------------
-// CONSOLE
-// TEST[continued]
-
-Now that we have seen a few of the basic search parameters, let's dig in some more into the Query DSL. Let's first take a look at the returned document fields. By default, the full JSON document is returned as part of all searches. This is referred to as the source (`_source` field in the search hits). If we don't want the entire source document returned, we have the ability to request only a few fields from within source to be returned.
-
-This example shows how to return two fields, `account_number` and `balance` (inside of `_source`), from the search:
-
-[source,js]
---------------------------------------------------
-GET /bank/_search
-{
-  "query": { "match_all": {} },
-  "_source": ["account_number", "balance"]
-}
---------------------------------------------------
-// CONSOLE
-// TEST[continued]
-
-Note that the above example simply reduces the `_source` field. It will still only return one field named `_source` but within it, only the fields `account_number` and `balance` are included.
+Now that you've seen how to submit a basic search request, you can start to
+construct queries that are a bit more interesting than `match_all`.
 
-If you come from a SQL background, the above is somewhat similar in concept to the `SQL SELECT FROM` field list.
-
-Now let's move on to the query part. Previously, we've seen how the `match_all` query is used to match all documents. Let's now introduce a new query called the {ref}/query-dsl-match-query.html[`match` query], which can be thought of as a basic fielded search query (i.e. a search done against a specific field or set of fields).
-
-This example returns the account numbered 20:
-
-[source,js]
---------------------------------------------------
-GET /bank/_search
-{
-  "query": { "match": { "account_number": 20 } }
-}
---------------------------------------------------
-// CONSOLE
-// TEST[continued]
-
-This example returns all accounts containing the term "mill" in the address:
-
-[source,js]
---------------------------------------------------
-GET /bank/_search
-{
-  "query": { "match": { "address": "mill" } }
-}
---------------------------------------------------
-// CONSOLE
-// TEST[continued]
-
-This example returns all accounts containing the term "mill" or "lane" in the address:
+To search for specific terms within a field, you can use a `match` query. 
+For example, the following request searches the `address` field to find 
+customers whose addresses contain `mill` or `lane`:
 
 [source,js]
 --------------------------------------------------
@@ -594,7 +461,9 @@ GET /bank/_search
 // CONSOLE
 // TEST[continued]
 
-This example is a variant of `match` (`match_phrase`) that returns all accounts containing the phrase "mill lane" in the address:
+To perform a phrase search rather than matching individual terms, you use
+`match_phrase` instead of `match`. For example, the following request only 
+matches addresses that contain the phrase `mill lane`: 
 
 [source,js]
 --------------------------------------------------
@@ -606,74 +475,13 @@ GET /bank/_search
 // CONSOLE
 // TEST[continued]
 
-Let's now introduce the {ref}/query-dsl-bool-query.html[`bool` query]. The `bool` query allows us to compose smaller queries into bigger queries using boolean logic.
-
-This example composes two `match` queries and returns all accounts containing "mill" and "lane" in the address:
-
-[source,js]
---------------------------------------------------
-GET /bank/_search
-{
-  "query": {
-    "bool": {
-      "must": [
-        { "match": { "address": "mill" } },
-        { "match": { "address": "lane" } }
-      ]
-    }
-  }
-}
---------------------------------------------------
-// CONSOLE
-// TEST[continued]
-
-In the above example, the `bool must` clause specifies all the queries that must be true for a document to be considered a match.
-
-In contrast, this example composes two `match` queries and returns all accounts containing "mill" or "lane" in the address:
-
-[source,js]
---------------------------------------------------
-GET /bank/_search
-{
-  "query": {
-    "bool": {
-      "should": [
-        { "match": { "address": "mill" } },
-        { "match": { "address": "lane" } }
-      ]
-    }
-  }
-}
---------------------------------------------------
-// CONSOLE
-// TEST[continued]
-
-In the above example, the `bool should` clause specifies a list of queries either of which must be true for a document to be considered a match.
+To construct more complex queries, you can use a `bool` query to combine
+multiple query criteria. You can designate criteria as required (must match), 
+desirable (should match), or undesirable (must not match).
 
-This example composes two `match` queries and returns all accounts that contain neither "mill" nor "lane" in the address:
-
-[source,js]
---------------------------------------------------
-GET /bank/_search
-{
-  "query": {
-    "bool": {
-      "must_not": [
-        { "match": { "address": "mill" } },
-        { "match": { "address": "lane" } }
-      ]
-    }
-  }
-}
---------------------------------------------------
-// CONSOLE
-// TEST[continued]
-
-In the above example, the `bool must_not` clause specifies a list of queries none of which must be true for a document to be considered a match.
-
-We can combine `must`, `should`, and `must_not` clauses simultaneously inside a `bool` query. Furthermore, we can compose `bool` queries inside any of these `bool` clauses to mimic any complex multi-level boolean logic.
-
-This example returns all accounts of anybody who is 40 years old but doesn't live in ID(aho):
+For example, the following request searches the `bank` index for accounts that
+belong to customers who are 40 years old, but excludes anyone who lives in
+Idaho (ID):
 
 [source,js]
 --------------------------------------------------
@@ -694,17 +502,19 @@ GET /bank/_search
 // CONSOLE
 // TEST[continued]
 
-[float]
-[[getting-started-filters]]
-=== Executing filters
-
-In the previous section, we skipped over a little detail called the document score (`_score` field in the search results). The score is a numeric value that is a relative measure of how well the document matches the search query that we specified. The higher the score, the more relevant the document is, the lower the score, the less relevant the document is.
-
-But queries do not always need to produce scores, in particular when they are only used for "filtering" the document set. Elasticsearch detects these situations and automatically optimizes query execution in order not to compute useless scores.
+Each `must`, `should`, and `must_not` element in a Boolean query is referred
+to as a query clause. How well a document meets the criteria in each `must` or
+`should` clause contributes to the document's _relevance score_. The higher the
+score, the better the document matches your search criteria. By default, {es}
+returns documents ranked by these relevance scores. 
 
-The {ref}/query-dsl-bool-query.html[`bool` query] that we introduced in the previous section also supports `filter` clauses which allow us to use a query to restrict the documents that will be matched by other clauses, without changing how scores are computed. As an example, let's introduce the {ref}/query-dsl-range-query.html[`range` query], which allows us to filter documents by a range of values. This is generally used for numeric or date filtering.
+The criteria in a `must_not` clause is treated as a _filter_. It affects whether
+or not the document is included in the results, but does not contribute to
+how documents are scored. You can also explicitly specify arbitrary filters to
+include or exclude documents based on structured data. 
 
-This example uses a bool query to return all accounts with balances between 20000 and 30000, inclusive. In other words, we want to find accounts with a balance that is greater than or equal to 20000 and less than or equal to 30000.
+For example, the following request uses a range filter to limit the results to
+accounts with a balance between $20,000 and $30,000 (inclusive). 
 
 [source,js]
 --------------------------------------------------
@@ -728,16 +538,18 @@ GET /bank/_search
 // CONSOLE
 // TEST[continued]
 
-Dissecting the above, the bool query contains a `match_all` query (the query part) and a `range` query (the filter part). We can substitute any other queries into the query and the filter parts. In the above case, the range query makes perfect sense since documents falling into the range all match "equally", i.e., no document is more relevant than another.
-
-In addition to the `match_all`, `match`, `bool`, and `range` queries, there are a lot of other query types that are available and we won't go into them here. Since we already have a basic understanding of how they work, it shouldn't be too difficult to apply this knowledge in learning and experimenting with the other query types.
-
 [[getting-started-aggregations]]
 == Analyze results with aggregations
 
-Aggregations provide the ability to group and extract statistics from your data. The easiest way to think about aggregations is by roughly equating it to the SQL GROUP BY and the SQL aggregate functions. In Elasticsearch, you have the ability to execute searches returning hits and at the same time return aggregated results separate from the hits all in one response. This is very powerful and efficient in the sense that you can run queries and multiple aggregations and get the results back of both (or either) operations in one shot avoiding network roundtrips using a concise and simplified API.
+{es} aggregations enable you to get meta-information about your search results
+and answer questions like, "How many account holders are in Texas?" or 
+"What's the average balance of accounts in Tennessee?" You can search 
+documents, filter hits, and use aggregations to analyze the results all in one
+request. 
 
-To start with, this example groups all the accounts by state, and then returns the top 10 (default) states sorted by count descending (also default):
+For example, the following request uses a `terms` aggregation to group
+all of the accounts in the `bank` index by state, and returns the ten states
+with the most accounts in descending order:
 
 [source,js]
 --------------------------------------------------
@@ -756,14 +568,10 @@ GET /bank/_search
 // CONSOLE
 // TEST[continued]
 
-In SQL, the above aggregation is similar in concept to:
-
-[source,sh]
---------------------------------------------------
-SELECT state, COUNT(*) FROM bank GROUP BY state ORDER BY COUNT(*) DESC LIMIT 10;
---------------------------------------------------
-
-And the response (partially shown):
+The `buckets` in the response are the values of the `state` field. The 
+`doc_count` shows the number of accounts in each state. For example, you
+can see that there are 27 accounts in `ID` (Idaho). Because the request
+set `size=0`, the response only contains the aggregation results.
 
 [source,js]
 --------------------------------------------------
@@ -825,12 +633,11 @@ And the response (partially shown):
 --------------------------------------------------
 // TESTRESPONSE[s/"took": 29/"took": $body.took/]
 
-We can see that there are 27 accounts in `ID` (Idaho), followed by 27 accounts
-in `TX` (Texas), followed by 25 accounts in `AL` (Alabama), and so forth.
-
-Note that we set `size=0` to not show search hits because we only want to see the aggregation results in the response.
 
-Building on the previous aggregation, this example calculates the average account balance by state (again only for the top 10 states sorted by count in descending order):
+You can combine aggregations to build more complex summaries of your data. For 
+example, the following request nests an `avg` aggregation within the previous
+`group_by_state` aggregation to calculate the average account balances for
+each state.
 
 [source,js]
 --------------------------------------------------
@@ -856,9 +663,8 @@ GET /bank/_search
 // CONSOLE
 // TEST[continued]
 
-Notice how we nested the `average_balance` aggregation inside the `group_by_state` aggregation. This is a common pattern for all the aggregations. You can nest aggregations inside aggregations arbitrarily to extract pivoted summarizations that you require from your data.
-
-Building on the previous aggregation, let's now sort on the average balance in descending order:
+Instead of sorting the results by count, you could sort using the result of
+the nested aggregation by specifying the order within the `terms` aggregation:
 
 [source,js]
 --------------------------------------------------
@@ -887,54 +693,14 @@ GET /bank/_search
 // CONSOLE
 // TEST[continued]
 
-This example demonstrates how we can group by age brackets (ages 20-29, 30-39, and 40-49), then by gender, and then finally get the average account balance, per age bracket, per gender:
-
-[source,js]
---------------------------------------------------
-GET /bank/_search
-{
-  "size": 0,
-  "aggs": {
-    "group_by_age": {
-      "range": {
-        "field": "age",
-        "ranges": [
-          {
-            "from": 20,
-            "to": 30
-          },
-          {
-            "from": 30,
-            "to": 40
-          },
-          {
-            "from": 40,
-            "to": 50
-          }
-        ]
-      },
-      "aggs": {
-        "group_by_gender": {
-          "terms": {
-            "field": "gender.keyword"
-          },
-          "aggs": {
-            "average_balance": {
-              "avg": {
-                "field": "balance"
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-}
---------------------------------------------------
-// CONSOLE
-// TEST[continued]
+In addition to basic bucketing and metrics aggregations like these, {es}
+provides specialized aggregations for operating on multiple fields and 
+analyzing particular types of data such as dates, IP addresses, and geo 
+data. You can also feed the results of individual aggregations into pipeline
+aggregations for further analysis.
 
-There are many other aggregations capabilities that we won't go into detail here. The {ref}/search-aggregations.html[aggregations reference guide] is a great starting point if you want to do further experimentation.
+The core analysis capabilities provided by aggregations enable advanced
+features such as using machine learning to detect anomalies. 
 
 [[getting-started-next-steps]]
 == Where to go from here

+ 3 - 0
docs/reference/indices.asciidoc

@@ -12,6 +12,7 @@ index settings, aliases, mappings, and index templates.
 * <<indices-delete-index>>
 * <<indices-get-index>>
 * <<indices-exists>>
+* <<indices-close>>
 * <<indices-open-close>>
 * <<indices-shrink-index>>
 * <<indices-split-index>>
@@ -67,6 +68,8 @@ include::indices/get-index.asciidoc[]
 
 include::indices/indices-exists.asciidoc[]
 
+include::indices/close.asciidoc[]
+
 include::indices/open-close.asciidoc[]
 
 include::indices/shrink-index.asciidoc[]

+ 85 - 0
docs/reference/indices/close.asciidoc

@@ -0,0 +1,85 @@
+[[indices-close]]
+=== Close index API
+++++
+<titleabbrev>Close index</titleabbrev>
+++++
+
+Closes an index.
+
+[source,js]
+--------------------------------------------------
+POST /twitter/_close
+--------------------------------------------------
+// CONSOLE
+// TEST[setup:twitter]
+
+
+[[close-index-api-request]]
+==== {api-request-title}
+
+`POST /<index>/_close`
+
+
+[[close-index-api-desc]]
+==== {api-description-title}
+
+You use the close index API to close open indices.
+
+include::{docdir}/indices/open-close.asciidoc[tag=closed-index]
+
+
+[[close-index-api-path-params]]
+==== {api-path-parms-title}
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=index]
++
+To close all indices, use `_all` or `*`.
+To disallow the closing of indices with `_all` or wildcard expressions,
+change the `action.destructive_requires_name` cluster setting to `true`.
+You can update this setting in the `elasticsearch.yml` file
+or using the <<cluster-update-settings,cluster update settings>> API.
+
+
+[[close-index-api-query-params]]
+==== {api-query-parms-title}
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=allow-no-indices]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=expand-wildcards]
++
+Defaults to `open`.
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=index-ignore-unavailable]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=doc-wait-for-active-shards]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=timeoutparms]
+
+
+[[close-index-api-example]]
+==== {api-examples-title}
+
+The following example shows how to close an index:
+
+[source,js]
+--------------------------------------------------
+POST /my_index/_close
+--------------------------------------------------
+// CONSOLE
+// TEST[s/^/PUT my_index\n/]
+
+The API returns following response:
+
+[source,js]
+--------------------------------------------------
+{
+    "acknowledged" : true,
+    "shards_acknowledged" : true,
+    "indices" : {
+        "my_index" : {
+            "closed" : true
+        }
+    }
+}
+--------------------------------------------------
+// TESTRESPONSE

+ 78 - 27
docs/reference/indices/create-index.asciidoc

@@ -1,23 +1,42 @@
 [[indices-create-index]]
-=== Create Index
+=== Create index API
+++++
+<titleabbrev>Create index</titleabbrev>
+++++
 
-The Create Index API is used to manually create an index in Elasticsearch.  All documents in Elasticsearch
-are stored inside of one index or another.
-
-The most basic command is the following:
+Creates a new index.
 
 [source,js]
 --------------------------------------------------
-PUT twitter
+PUT /twitter
 --------------------------------------------------
 // CONSOLE
 
-This creates an index named `twitter` with all default setting.
 
-[NOTE]
-.Index name limitations
-======================================================
-There are several limitations to what you can name your index.  The complete list of limitations are:
+[[indices-create-api-request]]
+==== {api-request-title}
+
+`PUT /<index>`
+
+[[indices-create-api-desc]]
+==== {api-description-title}
+You can use the create index API to add a new index to an {es} cluster. When 
+creating an index, you can specify the following:
+
+* Settings for the index
+* Mappings for fields in the index
+* Index aliases
+
+
+[[indices-create-api-path-params]]
+==== {api-path-parms-title}
+
+`<index>`::
++
+--
+(Optional, string) Name of the index you wish to create.
+
+Index names must meet the following criteria:
 
 - Lowercase only
 - Cannot include `\`, `/`, `*`, `?`, `"`, `<`, `>`, `|`, ` ` (space character), `,`, `#`
@@ -25,19 +44,55 @@ There are several limitations to what you can name your index.  The complete lis
 - Cannot start with `-`, `_`, `+`
 - Cannot be `.` or `..`
 - Cannot be longer than 255 bytes (note it is bytes, so multi-byte characters will count towards the 255 limit faster)
+--
+
+
+[[indices-create-api-query-params]]
+==== {api-query-parms-title}
 
-======================================================
+include::{docdir}/rest-api/common-parms.asciidoc[tag=include-type-name]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=doc-wait-for-active-shards]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=timeoutparms]
+
+
+[[indices-create-api-request-body]]
+==== {api-request-body-title}
+
+`aliases`::
+(Optional, <<indices-aliases,alias object>>) Index aliases which include the
+index. See <<indices-aliases>>.
+
+`mappings`::
++
+--
+(Optional, <<mapping,mapping object>>) Mapping for fields in the index. If
+specified, this mapping can include:
+
+* Field names
+* <<mapping-types,Field datatypes>>
+* <<mapping-params,Mapping parameters>>
+
+See <<mapping>>.
+--
+
+`settings`::
+(Optional, <<index-modules-settings,index setting object>>) Configuration
+options for the index. See <<index-modules-settings>>.
+
+[[indices-create-api-example]]
+==== {api-examples-title}
 
-[float]
 [[create-index-settings]]
-==== Index Settings
+===== Index settings
 
 Each index created can have specific settings
 associated with it, defined in the body:
 
 [source,js]
 --------------------------------------------------
-PUT twitter
+PUT /twitter
 {
     "settings" : {
         "index" : {
@@ -55,7 +110,7 @@ or more simplified
 
 [source,js]
 --------------------------------------------------
-PUT twitter
+PUT /twitter
 {
     "settings" : {
         "number_of_shards" : 3,
@@ -73,16 +128,14 @@ For more information regarding all the different index level settings
 that can be set when creating an index, please check the
 <<index-modules,index modules>> section.
 
-
-[float]
 [[mappings]]
-==== Mappings
+===== Mappings
 
 The create index API allows for providing a mapping definition:
 
 [source,js]
 --------------------------------------------------
-PUT test
+PUT /test
 {
     "settings" : {
         "number_of_shards" : 1
@@ -100,15 +153,14 @@ NOTE: Before 7.0.0, the 'mappings' definition used to include a type name. Altho
 types in requests is now deprecated, a type can still be provided if the request parameter
 include_type_name is set. For more details, please see <<removal-of-types>>.
 
-[float]
 [[create-index-aliases]]
-==== Aliases
+===== Aliases
 
 The create index API allows also to provide a set of <<indices-aliases,aliases>>:
 
 [source,js]
 --------------------------------------------------
-PUT test
+PUT /test
 {
     "aliases" : {
         "alias_1" : {},
@@ -123,9 +175,8 @@ PUT test
 --------------------------------------------------
 // CONSOLE
 
-[float]
 [[create-index-wait-for-active-shards]]
-==== Wait For Active Shards
+===== Wait For active shards
 
 By default, index creation will only return a response to the client when the primary copies of
 each shard have been started, or the request times out. The index creation response will indicate
@@ -158,7 +209,7 @@ the `wait_for_active_shards` value on all subsequent write operations):
 
 [source,js]
 --------------------------------------------------
-PUT test
+PUT /test
 {
     "settings": {
         "index.write.wait_for_active_shards": "2"
@@ -172,7 +223,7 @@ or through the request parameter `wait_for_active_shards`:
 
 [source,js]
 --------------------------------------------------
-PUT test?wait_for_active_shards=2
+PUT /test?wait_for_active_shards=2
 --------------------------------------------------
 // CONSOLE
 // TEST[skip:requires two nodes]

+ 42 - 10
docs/reference/indices/delete-index.asciidoc

@@ -1,7 +1,10 @@
 [[indices-delete-index]]
-=== Delete Index
+=== Delete index API
+++++
+<titleabbrev>Delete index</titleabbrev>
+++++
 
-The delete index API allows to delete an existing index.
+Deletes an existing index.
 
 [source,js]
 --------------------------------------------------
@@ -10,13 +13,42 @@ DELETE /twitter
 // CONSOLE
 // TEST[setup:twitter]
 
-The above example deletes an index called `twitter`. Specifying an index or a
-wildcard expression is required. Aliases cannot be used to delete an index.
-Wildcard expressions are resolved to matching concrete indices only.
 
-The delete index API can also be applied to more than one index, by either
-using a comma separated list, or on all indices (be careful!) by using `_all` or `*` as index.
+[[delete-index-api-request]]
+==== {api-request-title}
 
-In order to disable allowing to delete indices via wildcards or `_all`,
-set `action.destructive_requires_name` setting in the config to `true`.
-This setting can also be changed via the cluster update settings api.
+`DELETE /<index>`
+
+
+[[delete-index-api-path-params]]
+==== {api-path-parms-title}
+
+`<index>`::
++
+--
+(Request, string) Comma-separated list or wildcard expression of indices to
+delete.
+
+In this parameter, wildcard expressions match only open, concrete indices. You
+cannot delete an index using an <<indices-aliases,alias>>.
+
+To delete all indices, use `_all` or `*` . To disallow the deletion of indices
+with `_all` or wildcard expressions, change the
+`action.destructive_requires_name` cluster setting to `true`. You can update
+this setting in the `elasticsearch.yml` file or using the
+<<cluster-update-settings,cluster update settings>> API.
+--
+
+
+[[delete-index-api-query-params]]
+==== {api-query-parms-title}
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=allow-no-indices]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=expand-wildcards]
++
+Defaults to `open`.
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=index-ignore-unavailable]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=timeoutparms]

+ 86 - 38
docs/reference/indices/get-field-mapping.asciidoc

@@ -1,15 +1,70 @@
 [[indices-get-field-mapping]]
-=== Get Field Mapping
+=== Get field mapping API
+++++
+<titleabbrev>Get field mapping</titleabbrev>
+++++
 
-The get field mapping API allows you to retrieve mapping definitions for one or more fields.
-This is useful when you do not need the complete type mapping returned by
-the <<indices-get-mapping>> API.
+Retrieves <<mapping,mapping definitions>> for one or more fields. This is useful
+if you don't need the <<indices-get-mapping,complete mapping>> of an index or
+your index contains a large number of fields.
 
-For example, consider the following mapping:
+[source,js]
+----
+GET /twitter/_mapping/field/user
+----
+// CONSOLE
+// TEST[setup:twitter]
+
+
+[[get-field-mapping-api-request]]
+==== {api-request-title}
+
+`GET /_mapping/field/<field>`
+
+`GET /<index>/_mapping/field/<field>`
+
+
+[[get-field-mapping-api-path-params]]
+==== {api-path-parms-title}
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=index]
+
+`<field>`::
+(Optional, string) Comma-separated list or wildcard expression of fields used to
+limit returned information.
+
+
+[[get-field-mapping-api-query-params]]
+==== {api-query-parms-title}
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=allow-no-indices]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=expand-wildcards]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=include-type-name]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=index-ignore-unavailable]
+
+`include_defaults`::
+(Optional, boolean) If `true`, the response includes default mapping values.
+Defaults to `false`.
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=local]
+
+
+[[get-field-mapping-api-example]]
+==== {api-examples-title}
+
+[[get-field-mapping-api-basic-ex]]
+===== Example with index setup
+
+You can provide field mappings when creating a new index. The following
+<<indices-create-index, create index>> API request creates the `publications`
+index with several field mappings.
 
 [source,js]
 --------------------------------------------------
-PUT publications
+PUT /publications
 {
     "mappings": {
         "properties": {
@@ -26,7 +81,6 @@ PUT publications
     }
 }
 --------------------------------------------------
-// TESTSETUP
 // CONSOLE
 
 The following returns the mapping of the field `title` only:
@@ -36,8 +90,9 @@ The following returns the mapping of the field `title` only:
 GET publications/_mapping/field/title
 --------------------------------------------------
 // CONSOLE
+// TEST[continued]
 
-For which the response is:
+The API returns the following response:
 
 [source,js]
 --------------------------------------------------
@@ -58,30 +113,8 @@ For which the response is:
 --------------------------------------------------
 // TESTRESPONSE
 
-[float]
-==== Multiple Indices and Fields
-
-The get field mapping API can be used to get the mapping of multiple fields from more than one index
-with a single call. General usage of the API follows the
-following syntax: `host:port/{index}/_mapping/field/{field}` where
-`{index}` and `{field}` can stand for comma-separated list of names or wild cards. To
-get mappings for all indices you can use `_all` for `{index}`. The
-following are some examples:
-
-[source,js]
---------------------------------------------------
-GET /twitter,kimchy/_mapping/field/message
-
-GET /_all/_mapping/field/message,user.id
-
-GET /_all/_mapping/field/*.id
---------------------------------------------------
-// CONSOLE
-// TEST[setup:twitter]
-// TEST[s/^/PUT kimchy\nPUT book\n/]
-
-[float]
-==== Specifying fields
+[[get-field-mapping-api-specific-fields-ex]]
+===== Specifying fields
 
 The get mapping api allows you to specify a comma-separated list of fields.
 
@@ -92,6 +125,7 @@ For instance to select the `id` of the `author` field, you must use its full nam
 GET publications/_mapping/field/author.id,abstract,name
 --------------------------------------------------
 // CONSOLE
+// TEST[continued]
 
 returns:
 
@@ -129,6 +163,7 @@ The get field mapping API also supports wildcard notation.
 GET publications/_mapping/field/a*
 --------------------------------------------------
 // CONSOLE
+// TEST[continued]
 
 returns:
 
@@ -167,11 +202,24 @@ returns:
 --------------------------------------------------
 // TESTRESPONSE
 
-[float]
-==== Other options
+[[get-field-mapping-api-multi-index-ex]]
+===== Multiple indices and fields
 
-[horizontal]
-`include_defaults`::
+The get field mapping API can be used to get the mapping of multiple fields from more than one index
+with a single call. General usage of the API follows the
+following syntax: `host:port/<index>/_mapping/field/<field>` where
+`<index>` and `<field>` can stand for comma-separated list of names or wild cards. To
+get mappings for all indices you can use `_all` for `<index>`. The
+following are some examples:
+
+[source,js]
+--------------------------------------------------
+GET /twitter,kimchy/_mapping/field/message
 
-    adding `include_defaults=true` to the query string will cause the response
-    to include default values, which are normally suppressed.
+GET /_all/_mapping/field/message,user.id
+
+GET /_all/_mapping/field/*.id
+--------------------------------------------------
+// CONSOLE
+// TEST[setup:twitter]
+// TEST[s/^/PUT kimchy\nPUT book\n/]

+ 46 - 8
docs/reference/indices/get-index.asciidoc

@@ -1,7 +1,10 @@
 [[indices-get-index]]
-=== Get Index
+=== Get index API
+++++
+<titleabbrev>Get index</titleabbrev>
+++++
 
-The get index API allows to retrieve information about one or more indexes.
+Returns information about one or more indexes.
 
 [source,js]
 --------------------------------------------------
@@ -10,12 +13,47 @@ GET /twitter
 // CONSOLE
 // TEST[setup:twitter]
 
-The above example gets the information for an index called `twitter`. Specifying an index,
-alias or wildcard expression is required.
-
-The get index API can also be applied to more than one index, or on
-all indices by using `_all` or `*` as index.
-
 NOTE: Before 7.0.0, the 'mappings' definition used to include a type name. Although mappings
 in responses no longer contain a type name by default, you can still request the old format
 through the parameter include_type_name. For more details, please see <<removal-of-types>>.
+
+
+[[get-index-api-request]]
+==== {api-request-title}
+
+`GET /<index>`
+
+
+[[get-index-api-path-params]]
+==== {api-path-parms-title}
+
+`<index>`::
++
+--
+(Required, string) Comma-separated list or wildcard expression of index names
+used to limit the request.
+
+Use a value of `_all` to retrieve information for all indices in the cluster.
+--
+
+
+[[get-index-api-query-params]]
+==== {api-query-parms-title}
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=allow-no-indices]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=expand-wildcards]
++
+Defaults to `open`.
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=flat-settings]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=include-defaults]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=include-type-name]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=index-ignore-unavailable]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=local]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=master-timeout]

+ 1 - 1
docs/reference/indices/get-mapping.asciidoc

@@ -23,7 +23,7 @@ through the parameter `include_type_name`. For more details, please see <<remova
 
 `GET /_mapping`
 
-`GET /{index}/_mapping`
+`GET /<index>/_mapping`
 
 
 [[get-mapping-api-path-params]]

+ 53 - 11
docs/reference/indices/get-settings.asciidoc

@@ -1,7 +1,10 @@
 [[indices-get-settings]]
-=== Get Settings
+=== Get index settings API
+++++
+<titleabbrev>Get index settings</titleabbrev>
+++++
 
-The get settings API allows to retrieve settings of index/indices:
+Returns setting information for an index.
 
 [source,js]
 --------------------------------------------------
@@ -10,14 +13,54 @@ GET /twitter/_settings
 // CONSOLE
 // TEST[setup:twitter]
 
-[float]
-==== Multiple Indices and Types
 
-The get settings API can be used to get settings for more than one index
-with a single call. General usage of the API follows the
-following syntax: `host:port/{index}/_settings` where
-`{index}` can stand for comma-separated list of index names and aliases. To
-get settings for all indices you can use `_all` for `{index}`.
+[[get-index-settings-api-request]]
+==== {api-request-title}
+
+`GET /<index>/_settings`
+
+`GET /<index>/_settings/<setting>`
+
+
+[[get-index-settings-api-path-params]]
+==== {api-path-parms-title}
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=index]
++
+Use a value of `_all` to retrieve information for all indices in the cluster.
+
+`<setting>`::
+(Optional, string) Comma-separated list or wildcard expression of setting names
+used to limit the request.
+
+
+[[get-index-settings-api-query-params]]
+==== {api-query-parms-title}
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=allow-no-indices]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=expand-wildcards]
++
+Defaults to `all`.
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=flat-settings]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=include-defaults]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=index-ignore-unavailable]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=local]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=master-timeout]
+
+
+[[get-index-settings-api-example]]
+==== {api-examples-title}
+
+===== Multiple indices
+
+The get settings API can be used to get settings for more than one index with a
+single call. To get settings for all indices you can use `_all` for `<index>`.
 Wildcard expressions are also supported. The following are some examples:
 
 [source,js]
@@ -32,8 +75,7 @@ GET /log_2013_*/_settings
 // TEST[setup:twitter]
 // TEST[s/^/PUT kimchy\nPUT log_2013_01_01\n/]
 
-[float]
-==== Filtering settings by name
+===== Filtering settings by name
 
 The settings that are returned can be filtered with wildcard matching
 as follows:

+ 48 - 6
docs/reference/indices/indices-exists.asciidoc

@@ -1,17 +1,59 @@
 [[indices-exists]]
-=== Indices Exists
+=== Indices exists API
+++++
+<titleabbrev>Indices exists</titleabbrev>
+++++
 
-Used to check if the index (indices) exists or not. For example:
+Checks if an index exists.
+The returned HTTP status code indicates if the index exists or not.
+A `404` means it does not exist, and `200` means it does.
 
 [source,js]
 --------------------------------------------------
-HEAD twitter
+HEAD /twitter
 --------------------------------------------------
 // CONSOLE
 // TEST[setup:twitter]
 
-The HTTP status code indicates if the index exists or not. A `404` means
-it does not exist, and `200` means it does.
 
-IMPORTANT: This request does not distinguish between an index and an alias,
+[[indices-exists-api-request]]
+==== {api-request-title}
+
+`HEAD /<index>`
+
+
+[[indices-exists-api-path-params]]
+==== {api-path-parms-title}
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=index]
++
+IMPORTANT: This parameter does not distinguish between an index name and <<indices-aliases,alias>>,
 i.e. status code `200` is also returned if an alias exists with that name.
+
+
+[[indices-exists-api-query-params]]
+==== {api-query-parms-title}
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=allow-no-indices]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=expand-wildcards]
++
+Defaults to `open`.
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=flat-settings]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=include-defaults]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=index-ignore-unavailable]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=local]
+
+
+[[indices-exists-api-response-codes]]
+==== {api-response-codes-title}
+
+`200`::
+Indicates all specified indices or index aliases exist.
+
+ `404`::
+Indicates one or more specified indices or index aliases **do not** exist.

+ 78 - 48
docs/reference/indices/open-close.asciidoc

@@ -1,8 +1,32 @@
 [[indices-open-close]]
-=== Open / Close Index API
+=== Open index API
+++++
+<titleabbrev>Open index</titleabbrev>
+++++
 
-The open and close index APIs allow to close an index, and later on
-opening it.
+Opens a closed index.
+
+[source,js]
+--------------------------------------------------
+POST /twitter/_open
+--------------------------------------------------
+// CONSOLE
+// TEST[setup:twitter]
+// TEST[s/^/POST \/twitter\/_close\n/]
+
+
+[[open-index-api-request]]
+==== {api-request-title}
+
+`POST /<index>/_open`
+
+
+[[open-index-api-desc]]
+==== {api-description-title}
+
+You use the open index API to re-open closed indices.
+
+// tag::closed-index[]
 
 A closed index is blocked for read/write operations and does not allow
 all operations that opened indices allow. It is not possible to index
@@ -18,34 +42,61 @@ data of opened/closed indices is automatically replicated by the
 cluster to ensure that enough shard copies are safely kept around
 at all times.
 
-The REST endpoint is `/{index}/_close` and `/{index}/_open`.
+You can open and close multiple indices. An error is thrown
+if the request explicitly refers to a missing index. This behaviour can be
+disabled using the `ignore_unavailable=true` parameter.
 
-The following example shows how to close an index:
+All indices can be opened or closed at once using `_all` as the index name
+or specifying patterns that identify them all (e.g. `*`).
 
-[source,js]
---------------------------------------------------
-POST /my_index/_close
---------------------------------------------------
-// CONSOLE
-// TEST[s/^/PUT my_index\n/]
+Identifying indices via wildcards or `_all` can be disabled by setting the
+`action.destructive_requires_name` flag in the config file to `true`.
+This setting can also be changed via the cluster update settings api.
 
-This will return the following response:
+Closed indices consume a significant amount of disk-space which can cause problems in managed environments. Closing indices can be disabled via the cluster settings
+API by setting `cluster.indices.close.enable` to `false`. The default is `true`.
 
-[source,js]
---------------------------------------------------
-{
-    "acknowledged" : true,
-    "shards_acknowledged" : true,
-    "indices" : {
-        "my_index" : {
-            "closed" : true
-        }
-    }
-}
---------------------------------------------------
-// TESTRESPONSE
+===== Wait For active shards
 
-A closed index can be reopened like this:
+Because opening or closing an index allocates its shards, the
+<<create-index-wait-for-active-shards,`wait_for_active_shards`>> setting on
+index creation applies to the `_open` and `_close` index actions as well.
+
+// end::closed-index[]
+
+
+[[open-index-api-path-params]]
+==== {api-path-parms-title}
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=index]
++
+To open all indices, use `_all` or `*`.
+To disallow the opening of indices with `_all` or wildcard expressions,
+change the `action.destructive_requires_name` cluster setting to `true`.
+You can update this setting in the `elasticsearch.yml` file
+or using the <<cluster-update-settings,cluster update settings>> API.
+
+
+[[open-index-api-query-params]]
+==== {api-query-parms-title}
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=allow-no-indices]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=expand-wildcards]
++
+Defaults to `closed`.
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=index-ignore-unavailable]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=doc-wait-for-active-shards]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=timeoutparms]
+
+
+[[open-index-api-example]]
+==== {api-examples-title}
+
+A closed index can be re-opened like this:
 
 [source,js]
 --------------------------------------------------
@@ -54,7 +105,7 @@ POST /my_index/_open
 // CONSOLE
 // TEST[s/^/PUT my_index\nPOST my_index\/_close\n/]
 
-which will yield the following response:
+The API returns the following response:
 
 [source,js]
 --------------------------------------------------
@@ -64,24 +115,3 @@ which will yield the following response:
 }
 --------------------------------------------------
 // TESTRESPONSE
-
-It is possible to open and close multiple indices. An error will be thrown
-if the request explicitly refers to a missing index. This behaviour can be
-disabled using the `ignore_unavailable=true` parameter.
-
-All indices can be opened or closed at once using `_all` as the index name
-or specifying patterns that identify them all (e.g. `*`).
-
-Identifying indices via wildcards or `_all` can be disabled by setting the
-`action.destructive_requires_name` flag in the config file to `true`.
-This setting can also be changed via the cluster update settings api.
-
-Closed indices consume a significant amount of disk-space which can cause problems in managed environments. Closing indices can be disabled via the cluster settings
-API by setting `cluster.indices.close.enable` to `false`. The default is `true`.
-
-[float]
-==== Wait For Active Shards
-
-Because opening or closing an index allocates its shards, the
-<<create-index-wait-for-active-shards,`wait_for_active_shards`>> setting on
-index creation applies to the `_open` and `_close` index actions as well.

+ 104 - 23
docs/reference/indices/put-mapping.asciidoc

@@ -1,14 +1,15 @@
 [[indices-put-mapping]]
-=== Put Mapping
+=== Put mapping API
+++++
+<titleabbrev>Put mapping</titleabbrev>
+++++
 
-The PUT mapping API allows you to add fields to an existing index or to change search only settings of existing fields.
+Adds new fields to an existing index or changes the search settings of existing
+fields.
 
 [source,js]
---------------------------------------------------
-PUT twitter <1>
-{}
-
-PUT twitter/_mapping <2>
+----
+PUT /twitter/_mapping
 {
   "properties": {
     "email": {
@@ -16,19 +17,99 @@ PUT twitter/_mapping <2>
     }
   }
 }
---------------------------------------------------
+----
+// CONSOLE
+// TEST[setup:twitter]
+
+NOTE: Before 7.0.0, the 'mappings' definition used to include a type name.
+Although specifying types in requests is now deprecated, a type can still be
+provided if the request parameter `include_type_name` is set. For more details,
+please see <<removal-of-types>>.
+
+
+[[put-mapping-api-request]]
+==== {api-request-title}
+
+`PUT /<index>/_mapping`
+
+`PUT /_mapping`
+
+
+[[put-mapping-api-path-params]]
+==== {api-path-parms-title}
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=index]
++
+To update the mapping of all indices, omit this parameter or use a value of
+`_all`.
+
+
+[[put-mapping-api-query-params]]
+==== {api-query-parms-title}
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=allow-no-indices]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=expand-wildcards]
++
+Defaults to `open`.
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=include-type-name]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=index-ignore-unavailable]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=timeoutparms]
+
+
+[[put-mapping-api-request-body]]
+==== {api-request-body-title}
+
+`properties`::
++
+--
+(Required, <<mapping,mapping object>>) Mapping for a field. For new
+fields, this mapping can include:
+
+* Field name
+* <<field-datatypes,Field datatype>>
+* <<mapping-params,Mapping parameters>>
+
+For existing fields, see <<updating-field-mappings>>.
+--
+
+
+[[put-mapping-api-example]]
+==== {api-examples-title}
+
+[[put-field-mapping-api-basic-ex]]
+===== Example with index setup
+
+The put mapping API requires an existing index. The following
+<<indices-create-index, create index>> API request creates the `publications`
+index with no mapping.
+
+[source,js]
+----
+PUT /publications
+----
 // CONSOLE
-<1> <<indices-create-index,Creates an index>> called `twitter` without any mapping.
-<2> Uses the PUT mapping API to add a new field called `email`.
 
-More information on how to define mappings can be found in the <<mapping,mapping>> section.
+The following put mapping API request adds `title`, a new <<text,`text`>> field,
+to the `publications` index.
 
-NOTE: Before 7.0.0, the 'mappings' definition used to include a type name. Although specifying
-types in requests is now deprecated, a type can still be provided if the request parameter
-include_type_name is set. For more details, please see <<removal-of-types>>.
+[source,js]
+----
+PUT /publications/_mapping
+{
+  "properties": {
+    "title":  { "type": "text"}
+  }
+}
+----
+// CONSOLE
+// TEST[continued]
 
-[float]
-==== Multi-index
+[[put-mapping-api-multi-ex]]
+===== Multiple indices
 
 The PUT mapping API can be applied to multiple indices with a single request.
 For example, we can update the `twitter-1` and `twitter-2` mappings at the same time:
@@ -36,8 +117,8 @@ For example, we can update the `twitter-1` and `twitter-2` mappings at the same
 [source,js]
 --------------------------------------------------
 # Create the two indices
-PUT twitter-1
-PUT twitter-2
+PUT /twitter-1
+PUT /twitter-2
 
 # Update both mappings
 PUT /twitter-1,twitter-2/_mapping <1>
@@ -50,12 +131,12 @@ PUT /twitter-1,twitter-2/_mapping <1>
 }
 --------------------------------------------------
 // CONSOLE
-<1> Note that the indices specified (`twitter-1,twitter-2`) follows <<multi-index,multiple index names>> and wildcard format.
+// TEST[setup:twitter]
 
+<1> Note that the indices specified (`twitter-1,twitter-2`) follows <<multi-index,multiple index names>> and wildcard format.
 
 [[updating-field-mappings]]
-[float]
-==== Updating field mappings
+===== Update an existing field
 
 // tag::put-field-mapping-exceptions[]
 
@@ -79,7 +160,7 @@ For example:
 
 [source,js]
 -----------------------------------
-PUT my_index <1>
+PUT /my_index <1>
 {
   "mappings": {
     "properties": {
@@ -97,7 +178,7 @@ PUT my_index <1>
   }
 }
 
-PUT my_index/_mapping
+PUT /my_index/_mapping
 {
   "properties": {
     "name": {

+ 63 - 16
docs/reference/indices/update-settings.asciidoc

@@ -1,11 +1,10 @@
 [[indices-update-settings]]
-=== Update Indices Settings
+=== Update index settings API
+++++
+<titleabbrev>Update index settings</titleabbrev>
+++++
 
-Change specific index level settings in real time.
-
-The REST endpoint is `/_settings` (to update all indices) or
-`{index}/_settings` to update one (or more) indices settings.
-The body of the request includes the updated settings, for example:
+Changes an <<index-modules-settings,index setting>> in real time.
 
 [source,js]
 --------------------------------------------------
@@ -19,7 +18,55 @@ PUT /twitter/_settings
 // CONSOLE
 // TEST[setup:twitter]
 
-To reset a setting back to the default value, use `null`. For example:
+
+[[update-index-settings-api-request]]
+==== {api-request-title}
+
+`PUT /<index>/_settings`
+
+
+[[update-index-settings-api-path-params]]
+==== {api-path-parms-title}
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=index]
++
+To update a setting for all indices,
+use `_all` or exclude this parameter.
+
+
+[[update-index-settings-api-query-params]]
+==== {api-query-parms-title}
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=allow-no-indices]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=expand-wildcards]
++
+Defaults to `open`.
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=flat-settings]
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=index-ignore-unavailable]
+
+`preserve_existing`::
+(Optional, boolean) If `true`, existing index settings remain unchanged.
+Defaults to `false`.
+
+include::{docdir}/rest-api/common-parms.asciidoc[tag=timeoutparms]
+
+
+[[sample-api-query-params]]
+==== {api-query-parms-title}
+
+`settings`::
+(Optional, <<index-modules-settings,index setting object>>) Configuration
+options for the index. See <<index-modules-settings>>.
+
+[[sample-api-example]]
+==== {api-examples-title}
+
+[[reset-index-setting]]
+===== Reset an index setting
+To revert a setting to the default value, use `null`. For example:
 
 [source,js]
 --------------------------------------------------
@@ -38,9 +85,8 @@ indices can be found in <<index-modules>>.
 To preserve existing settings from being updated, the `preserve_existing`
 request parameter can be set to `true`.
 
-[float]
 [[bulk]]
-==== Bulk Indexing Usage
+===== Bulk indexing usage
 
 For example, the update settings API can be used to dynamically change
 the index from being more performant for bulk indexing, and then move it
@@ -86,16 +132,17 @@ POST /twitter/_forcemerge?max_num_segments=5
 // CONSOLE
 // TEST[continued]
 
-[float]
 [[update-settings-analysis]]
-==== Updating Index Analysis
+===== Update index analysis
 
-It is also possible to define new <<analysis,analyzers>> for the index.
-But it is required to <<indices-open-close,close>> the index
-first and <<indices-open-close,open>> it after the changes are made.
+You can only define new analyzers on closed indices.
 
-For example if `content` analyzer hasn't been defined on `myindex` yet
-you can use the following commands to add it:
+To add an analyzer,
+you must close the index,
+define the analyzer,
+and reopen the index.
+For example,
+the following commands add the `content` analyzer to `myindex`:
 
 [source,js]
 --------------------------------------------------

+ 15 - 0
docs/reference/ingest/apis/index.asciidoc

@@ -0,0 +1,15 @@
+[[ingest-apis]]
+== Ingest APIs
+
+The following ingest APIs are available for managing pipelines:
+
+* <<put-pipeline-api>> to add or update a pipeline
+* <<get-pipeline-api>> to return a specific pipeline
+* <<delete-pipeline-api>> to delete a pipeline
+* <<simulate-pipeline-api>> to simulate a call to a pipeline
+
+
+include::put-pipeline.asciidoc[]
+include::get-pipeline.asciidoc[]
+include::delete-pipeline.asciidoc[]
+include::simulate-pipeline.asciidoc[]

+ 0 - 15
docs/reference/ingest/ingest-node.asciidoc

@@ -20,21 +20,6 @@ what the pipeline does.
 The `processors` parameter defines a list of processors to be executed in
 order.
 
-[[ingest-apis]]
-== Ingest APIs
-
-The following ingest APIs are available for managing pipelines:
-
-* <<put-pipeline-api>> to add or update a pipeline
-* <<get-pipeline-api>> to return a specific pipeline
-* <<delete-pipeline-api>> to delete a pipeline
-* <<simulate-pipeline-api>> to simulate a call to a pipeline
-
-include::apis/put-pipeline.asciidoc[]
-include::apis/get-pipeline.asciidoc[]
-include::apis/delete-pipeline.asciidoc[]
-include::apis/simulate-pipeline.asciidoc[]
-
 [[accessing-data-in-pipelines]]
 == Accessing Data in Pipelines
 

+ 1 - 0
docs/reference/mapping.asciidoc

@@ -38,6 +38,7 @@ document.
 
 
 [float]
+[[field-datatypes]]
 == Field datatypes
 
 Each field has a data `type` which can be:

+ 18 - 2
docs/reference/migration/migrate_8_0/settings.asciidoc

@@ -13,11 +13,27 @@ refuse to start if you have these settings in your configuration or cluster
 state.
 
 [float]
-==== `processors` can no longer exceed the available number of processors
+[[remove-pidfile]]
+==== `pidfile` setting is replaced by `node.pidfile`
+
+To ensure that all settings are in a proper namespace, the `pidfile` setting was
+previously deprecated in version 7.4.0 of Elasticsearch, and is removed in
+version 8.0.0. Instead, use `node.pidfile`.
+
+[float]
+[[remove-processors]]
+==== `processors` setting is replaced by `node.processors`
+
+To ensure that all settings are in a proper namespace, the `processors` setting
+was previously deprecated in version 7.4.0 of Elasticsearch, and is removed in
+version 8.0.0. Instead, use `node.processors`.
+
+[float]
+==== `node.processors` can no longer exceed the available number of processors
 
 Previously it was possible to set the number of processors used to set the
 default sizes for the thread pools to be more than the number of available
 processors. As this leads to more context switches and more threads but without
 an increase in the number of physical CPUs on which to schedule these additional
-threads, the `processors` setting is now bounded by the number of available
+threads, the `node.processors` setting is now bounded by the number of available
 processors.

Some files were not shown because too many files changed in this diff