From 21fcc482eb4c1d8547f8fcd3bbf5f0558e49487d Mon Sep 17 00:00:00 2001 From: Alexander Reelsen Date: Fri, 3 May 2013 09:22:34 +0200 Subject: [PATCH] Allow to set headers in HTTP response This commit allows to set custom headers in HTTP responses (like setting the WWW-Authenticate header for basic auth) by adding RestRequest.addHeader() method. Closes #2936 Closes #2540 To get the history right: This is based on PR #2723 --- .../http/netty/NettyHttpChannel.java | 12 ++ .../rest/AbstractRestResponse.java | 25 +++ .../org/elasticsearch/rest/RestResponse.java | 9 ++ .../nodesinfo/SimpleNodesInfoTests.java | 153 +++++++++++------- .../plugin/ResponseHeaderPluginTests.java | 84 ++++++++++ .../TestResponseHeaderPlugin.java | 40 +++++ .../TestResponseHeaderRestAction.java | 46 ++++++ .../integration/rest/helper/HttpClient.java | 18 ++- .../rest/helper/HttpClientResponse.java | 22 ++- .../es-plugin.properties | 20 +++ 10 files changed, 366 insertions(+), 63 deletions(-) create mode 100644 src/test/java/org/elasticsearch/test/integration/plugin/ResponseHeaderPluginTests.java create mode 100644 src/test/java/org/elasticsearch/test/integration/plugin/responseheader/TestResponseHeaderPlugin.java create mode 100644 src/test/java/org/elasticsearch/test/integration/plugin/responseheader/TestResponseHeaderRestAction.java create mode 100644 src/test/resources/org/elasticsearch/test/integration/responseheader/responseheader-plugin/es-plugin.properties diff --git a/src/main/java/org/elasticsearch/http/netty/NettyHttpChannel.java b/src/main/java/org/elasticsearch/http/netty/NettyHttpChannel.java index 5e3508a84fa..68dcb61b310 100644 --- a/src/main/java/org/elasticsearch/http/netty/NettyHttpChannel.java +++ b/src/main/java/org/elasticsearch/http/netty/NettyHttpChannel.java @@ -36,6 +36,8 @@ import org.jboss.netty.channel.ChannelFutureListener; import org.jboss.netty.handler.codec.http.*; import java.io.IOException; +import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -90,6 +92,16 @@ public class NettyHttpChannel implements HttpChannel { resp.addHeader("X-Opaque-Id", opaque); } + // Add all custom headers + Map> customHeaders = response.getHeaders(); + if (customHeaders != null) { + for (Map.Entry> headerEntry : customHeaders.entrySet()) { + for (String headerValue : headerEntry.getValue()) { + resp.addHeader(headerEntry.getKey(), headerValue); + } + } + } + // Convert the response content to a ChannelBuffer. ChannelFutureListener releaseContentListener = null; ChannelBuffer buf; diff --git a/src/main/java/org/elasticsearch/rest/AbstractRestResponse.java b/src/main/java/org/elasticsearch/rest/AbstractRestResponse.java index 72592b255ce..53c3f460379 100644 --- a/src/main/java/org/elasticsearch/rest/AbstractRestResponse.java +++ b/src/main/java/org/elasticsearch/rest/AbstractRestResponse.java @@ -19,11 +19,18 @@ package org.elasticsearch.rest; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.HashMap; + /** * */ public abstract class AbstractRestResponse implements RestResponse { + Map> customHeaders; + @Override public byte[] prefixContent() { return null; @@ -53,4 +60,22 @@ public abstract class AbstractRestResponse implements RestResponse { public int suffixContentOffset() { return 0; } + + @Override + public void addHeader(String name, String value) { + if (customHeaders == null) { + customHeaders = new HashMap>(2); + } + List header = customHeaders.get(name); + if (header == null) { + header = new ArrayList(); + customHeaders.put(name, header); + } + header.add(value); + } + + @Override + public Map> getHeaders() { + return customHeaders; + } } \ No newline at end of file diff --git a/src/main/java/org/elasticsearch/rest/RestResponse.java b/src/main/java/org/elasticsearch/rest/RestResponse.java index 15bba829321..3700c5a0bcd 100644 --- a/src/main/java/org/elasticsearch/rest/RestResponse.java +++ b/src/main/java/org/elasticsearch/rest/RestResponse.java @@ -20,6 +20,8 @@ package org.elasticsearch.rest; import java.io.IOException; +import java.util.List; +import java.util.Map; /** * @@ -59,4 +61,11 @@ public interface RestResponse { int suffixContentOffset(); RestStatus status(); + + void addHeader(String name, String value); + + /** + * @return The custom headers or null if none have been set + */ + Map> getHeaders(); } diff --git a/src/test/java/org/elasticsearch/test/integration/nodesinfo/SimpleNodesInfoTests.java b/src/test/java/org/elasticsearch/test/integration/nodesinfo/SimpleNodesInfoTests.java index 52dad63f34a..4e09f03206e 100644 --- a/src/test/java/org/elasticsearch/test/integration/nodesinfo/SimpleNodesInfoTests.java +++ b/src/test/java/org/elasticsearch/test/integration/nodesinfo/SimpleNodesInfoTests.java @@ -19,6 +19,12 @@ package org.elasticsearch.test.integration.nodesinfo; +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse; import org.elasticsearch.action.admin.cluster.node.info.NodesInfoResponse; import org.elasticsearch.action.admin.cluster.node.info.PluginInfo; @@ -35,7 +41,10 @@ import org.testng.annotations.Test; import java.io.File; import java.net.URISyntaxException; import java.net.URL; +import java.util.Collections; +import java.util.List; +import static com.google.common.base.Predicates.*; import static org.elasticsearch.client.Requests.clusterHealthRequest; import static org.elasticsearch.client.Requests.nodesInfoRequest; import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder; @@ -72,29 +81,29 @@ public class SimpleNodesInfoTests extends AbstractNodesTests { logger.info("--> started nodes: " + server1NodeId + " and " + server2NodeId); NodesInfoResponse response = client("server1").admin().cluster().prepareNodesInfo().execute().actionGet(); - assertThat(response.getNodes().length, equalTo(2)); + assertThat(response.getNodes().length, is(2)); assertThat(response.getNodesMap().get(server1NodeId), notNullValue()); assertThat(response.getNodesMap().get(server2NodeId), notNullValue()); response = client("server2").admin().cluster().nodesInfo(nodesInfoRequest()).actionGet(); - assertThat(response.getNodes().length, equalTo(2)); + assertThat(response.getNodes().length, is(2)); assertThat(response.getNodesMap().get(server1NodeId), notNullValue()); assertThat(response.getNodesMap().get(server2NodeId), notNullValue()); response = client("server1").admin().cluster().nodesInfo(nodesInfoRequest(server1NodeId)).actionGet(); - assertThat(response.getNodes().length, equalTo(1)); + assertThat(response.getNodes().length, is(1)); assertThat(response.getNodesMap().get(server1NodeId), notNullValue()); response = client("server2").admin().cluster().nodesInfo(nodesInfoRequest(server1NodeId)).actionGet(); - assertThat(response.getNodes().length, equalTo(1)); + assertThat(response.getNodes().length, is(1)); assertThat(response.getNodesMap().get(server1NodeId), notNullValue()); response = client("server1").admin().cluster().nodesInfo(nodesInfoRequest(server2NodeId)).actionGet(); - assertThat(response.getNodes().length, equalTo(1)); + assertThat(response.getNodes().length, is(1)); assertThat(response.getNodesMap().get(server2NodeId), notNullValue()); response = client("server2").admin().cluster().nodesInfo(nodesInfoRequest(server2NodeId)).actionGet(); - assertThat(response.getNodes().length, equalTo(1)); + assertThat(response.getNodes().length, is(1)); assertThat(response.getNodesMap().get(server2NodeId), notNullValue()); } @@ -127,68 +136,64 @@ public class SimpleNodesInfoTests extends AbstractNodesTests { NodesInfoResponse response = client("node1").admin().cluster().prepareNodesInfo().setPlugin(true).execute().actionGet(); logger.info("--> full json answer, status " + response.toString()); - checkPlugin(response, server1NodeId, 0, 0); - checkPlugin(response, server2NodeId, 1, 0); - checkPlugin(response, server3NodeId, 0, 1); - checkPlugin(response, server4NodeId, 1, 2); // Note that we have now 2 JVM plugins as we have already loaded one with node3 + assertNodeContainsPlugins(response, server1NodeId, Collections.EMPTY_LIST, Collections.EMPTY_LIST, + Collections.EMPTY_LIST, Collections.EMPTY_LIST); + + assertNodeContainsPlugins(response, server2NodeId, Collections.EMPTY_LIST, Collections.EMPTY_LIST, + Lists.newArrayList(Fields.SITE_PLUGIN), + Lists.newArrayList(Fields.SITE_PLUGIN_DESCRIPTION)); + + assertNodeContainsPlugins(response, server3NodeId, Lists.newArrayList(TestPlugin.Fields.NAME), + Lists.newArrayList(TestPlugin.Fields.DESCRIPTION), + Collections.EMPTY_LIST, Collections.EMPTY_LIST); + + // Note that we have now 2 JVM plugins as we have already loaded one with node3 + assertNodeContainsPlugins(response, server4NodeId, + Lists.newArrayList(TestPlugin.Fields.NAME, TestNoVersionPlugin.Fields.NAME), + Lists.newArrayList(TestPlugin.Fields.DESCRIPTION, TestNoVersionPlugin.Fields.DESCRIPTION), + Lists.newArrayList(Fields.SITE_PLUGIN, TestNoVersionPlugin.Fields.NAME), + Lists.newArrayList(Fields.SITE_PLUGIN_NO_DESCRIPTION, TestNoVersionPlugin.Fields.DESCRIPTION)); } - /** - * We check infos - * @param response Response - * @param nodeId NodeId we want to check - * @param expectedSitePlugins Number of site plugins expected - * @param expectedJvmPlugins Number of jvm plugins expected - */ - private void checkPlugin(NodesInfoResponse response, String nodeId, - int expectedSitePlugins, int expectedJvmPlugins) { + private void assertNodeContainsPlugins(NodesInfoResponse response, String nodeId, + List expectedJvmPluginNames, + List expectedJvmPluginDescriptions, + List expectedSitePluginNames, + List expectedSitePluginDescriptions) { + assertThat(response.getNodesMap().get(nodeId), notNullValue()); PluginsInfo plugins = response.getNodesMap().get(nodeId).getPlugins(); assertThat(plugins, notNullValue()); - int num_site_plugins = 0; - int num_jvm_plugins = 0; - - for (PluginInfo pluginInfo : plugins.getInfos()) { - // It should be a site or a jvm plugin - assertThat(pluginInfo.isJvm() || pluginInfo.isSite(), is(true)); - - if (pluginInfo.isSite() && !pluginInfo.isJvm()) { - // Let's do some tests for site plugins - assertThat(pluginInfo.getName(), isOneOf(Fields.SITE_PLUGIN, - TestNoVersionPlugin.Fields.NAME)); - assertThat(pluginInfo.getDescription(), - isOneOf(Fields.SITE_PLUGIN_DESCRIPTION, - Fields.SITE_PLUGIN_NO_DESCRIPTION, - Fields.JVM_PLUGIN_NO_DESCRIPTION)); - assertThat(pluginInfo.getUrl(), notNullValue()); - num_site_plugins++; - } - - if (pluginInfo.isJvm() && !pluginInfo.isSite()) { - // Let's do some tests for site plugins - assertThat(pluginInfo.getName(), - isOneOf(TestPlugin.Fields.NAME, TestNoVersionPlugin.Fields.NAME)); - assertThat(pluginInfo.getDescription(), - isOneOf(TestPlugin.Fields.DESCRIPTION, TestNoVersionPlugin.Fields.DESCRIPTION)); - assertThat(pluginInfo.getUrl(), nullValue()); - num_jvm_plugins++; - } - - // On node4, test-no-version-plugin has an embedded _site structure - if (pluginInfo.isJvm() && pluginInfo.isSite()) { - assertThat(pluginInfo.getName(), - is(TestNoVersionPlugin.Fields.NAME)); - assertThat(pluginInfo.getDescription(), - is(TestNoVersionPlugin.Fields.DESCRIPTION)); - assertThat(pluginInfo.getUrl(), notNullValue()); - num_jvm_plugins++; - } + List pluginNames = FluentIterable.from(plugins.getInfos()).filter(jvmPluginPredicate).transform(nameFunction).toList(); + for (String expectedJvmPluginName : expectedJvmPluginNames) { + assertThat(pluginNames, hasItem(expectedJvmPluginName)); } - assertThat(num_site_plugins, is(expectedSitePlugins)); - assertThat(num_jvm_plugins, is(expectedJvmPlugins)); + List pluginDescriptions = FluentIterable.from(plugins.getInfos()).filter(jvmPluginPredicate).transform(descriptionFunction).toList(); + for (String expectedJvmPluginDescription : expectedJvmPluginDescriptions) { + assertThat(pluginDescriptions, hasItem(expectedJvmPluginDescription)); + } + + FluentIterable jvmUrls = FluentIterable.from(plugins.getInfos()) + .filter(and(jvmPluginPredicate, Predicates.not(sitePluginPredicate))) + .filter(isNull()) + .transform(urlFunction); + assertThat(Iterables.size(jvmUrls), is(0)); + + List sitePluginNames = FluentIterable.from(plugins.getInfos()).filter(sitePluginPredicate).transform(nameFunction).toList(); + for (String expectedSitePluginName : expectedSitePluginNames) { + assertThat(sitePluginNames, hasItem(expectedSitePluginName)); + } + + List sitePluginDescriptions = FluentIterable.from(plugins.getInfos()).filter(sitePluginPredicate).transform(descriptionFunction).toList(); + for (String sitePluginDescription : expectedSitePluginDescriptions) { + assertThat(sitePluginDescriptions, hasItem(sitePluginDescription)); + } + + List sitePluginUrls = FluentIterable.from(plugins.getInfos()).filter(sitePluginPredicate).transform(urlFunction).toList(); + assertThat(sitePluginUrls, not(contains(nullValue()))); } private String startNodeWithPlugins(String name) throws URISyntaxException { @@ -209,4 +214,34 @@ public class SimpleNodesInfoTests extends AbstractNodesTests { return serverNodeId; } + + private Predicate jvmPluginPredicate = new Predicate() { + public boolean apply(PluginInfo pluginInfo) { + return pluginInfo.isJvm(); + } + }; + + private Predicate sitePluginPredicate = new Predicate() { + public boolean apply(PluginInfo pluginInfo) { + return pluginInfo.isSite(); + } + }; + + private Function nameFunction = new Function() { + public String apply(PluginInfo pluginInfo) { + return pluginInfo.getName(); + } + }; + + private Function descriptionFunction = new Function() { + public String apply(PluginInfo pluginInfo) { + return pluginInfo.getDescription(); + } + }; + + private Function urlFunction = new Function() { + public String apply(PluginInfo pluginInfo) { + return pluginInfo.getUrl(); + } + }; } diff --git a/src/test/java/org/elasticsearch/test/integration/plugin/ResponseHeaderPluginTests.java b/src/test/java/org/elasticsearch/test/integration/plugin/ResponseHeaderPluginTests.java new file mode 100644 index 00000000000..3b80e9b0903 --- /dev/null +++ b/src/test/java/org/elasticsearch/test/integration/plugin/ResponseHeaderPluginTests.java @@ -0,0 +1,84 @@ +/* + * Licensed to Elastic Search and Shay Banon 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.test.integration.plugin; + +import static org.elasticsearch.client.Requests.clusterHealthRequest; +import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +import com.beust.jcommander.internal.Maps; +import org.elasticsearch.common.settings.ImmutableSettings; +import org.elasticsearch.http.HttpServerTransport; +import org.elasticsearch.node.internal.InternalNode; +import org.elasticsearch.rest.RestStatus; +import org.elasticsearch.test.integration.AbstractNodesTests; +import org.elasticsearch.test.integration.rest.helper.HttpClient; +import org.elasticsearch.test.integration.rest.helper.HttpClientResponse; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.io.File; +import java.net.URL; +import java.util.Map; + +/** + * Test a rest action that sets special response headers + */ +public class ResponseHeaderPluginTests extends AbstractNodesTests { + + public static final String NODE_ID = "TEST"; + + @BeforeMethod + public void startNode() throws Exception { + URL resource = ResponseHeaderPluginTests.class.getResource("/org/elasticsearch/test/integration/responseheader/"); + ImmutableSettings.Builder settings = settingsBuilder(); + if (resource != null) { + settings.put("path.plugins", new File(resource.toURI()).getAbsolutePath()); + } + + startNode(NODE_ID, settings); + client(NODE_ID).admin().cluster().health(clusterHealthRequest().waitForGreenStatus()).actionGet(); + } + + @AfterMethod + public void closeNodes() { + closeAllNodes(); + } + + @Test + public void testThatSettingHeadersWorks() throws Exception { + HttpClientResponse response = httpClient().request("/_protected"); + assertThat(response.errorCode(), equalTo(RestStatus.UNAUTHORIZED.getStatus())); + assertThat(response.getHeader("Secret"), equalTo("required")); + + Map headers = Maps.newHashMap(); + headers.put("Secret", "password"); + HttpClientResponse authResponse = httpClient().request("GET", "_protected", headers); + assertThat(authResponse.errorCode(), equalTo(RestStatus.OK.getStatus())); + assertThat(authResponse.getHeader("Secret"), equalTo("granted")); + } + + private HttpClient httpClient() { + HttpServerTransport httpServerTransport = ((InternalNode) node(NODE_ID)).injector().getInstance(HttpServerTransport.class); + return new HttpClient(httpServerTransport.boundAddress().publishAddress()); + } +} \ No newline at end of file diff --git a/src/test/java/org/elasticsearch/test/integration/plugin/responseheader/TestResponseHeaderPlugin.java b/src/test/java/org/elasticsearch/test/integration/plugin/responseheader/TestResponseHeaderPlugin.java new file mode 100644 index 00000000000..9101edfc5d1 --- /dev/null +++ b/src/test/java/org/elasticsearch/test/integration/plugin/responseheader/TestResponseHeaderPlugin.java @@ -0,0 +1,40 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.test.integration.plugin.responseheader; + +import org.elasticsearch.plugins.AbstractPlugin; +import org.elasticsearch.rest.RestModule; + +public class TestResponseHeaderPlugin extends AbstractPlugin { + + @Override + public String name() { + return "test-plugin-custom-header"; + } + + @Override + public String description() { + return "test-plugin-custom-header-desc"; + } + + public void onModule(RestModule restModule) { + restModule.addRestAction(TestResponseHeaderRestAction.class); + } +} diff --git a/src/test/java/org/elasticsearch/test/integration/plugin/responseheader/TestResponseHeaderRestAction.java b/src/test/java/org/elasticsearch/test/integration/plugin/responseheader/TestResponseHeaderRestAction.java new file mode 100644 index 00000000000..1939aac8082 --- /dev/null +++ b/src/test/java/org/elasticsearch/test/integration/plugin/responseheader/TestResponseHeaderRestAction.java @@ -0,0 +1,46 @@ +/* + * Licensed to ElasticSearch and Shay Banon 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.test.integration.plugin.responseheader; + +import org.elasticsearch.client.Client; +import org.elasticsearch.common.inject.Inject; +import org.elasticsearch.common.settings.Settings; +import org.elasticsearch.rest.*; + +public class TestResponseHeaderRestAction extends BaseRestHandler { + + @Inject + public TestResponseHeaderRestAction(Settings settings, Client client, RestController controller) { + super(settings, client); + controller.registerHandler(RestRequest.Method.GET, "/_protected", this); + } + + @Override + public void handleRequest(RestRequest request, RestChannel channel) { + if ("password".equals(request.header("Secret"))) { + RestResponse response = new StringRestResponse(RestStatus.OK, "Access granted"); + response.addHeader("Secret", "granted"); + channel.sendResponse(response); + } else { + RestResponse response = new StringRestResponse(RestStatus.UNAUTHORIZED, "Access denied"); + response.addHeader("Secret", "required"); + channel.sendResponse(response); + } + } +} diff --git a/src/test/java/org/elasticsearch/test/integration/rest/helper/HttpClient.java b/src/test/java/org/elasticsearch/test/integration/rest/helper/HttpClient.java index 91ebe6276ae..895a1bbda8d 100644 --- a/src/test/java/org/elasticsearch/test/integration/rest/helper/HttpClient.java +++ b/src/test/java/org/elasticsearch/test/integration/rest/helper/HttpClient.java @@ -30,6 +30,8 @@ import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.MalformedURLException; import java.net.URL; +import java.util.Map; +import java.util.List; public class HttpClient { @@ -61,6 +63,10 @@ public class HttpClient { } public HttpClientResponse request(String method, String path) { + return request(method, path, null); + } + + public HttpClientResponse request(String method, String path, Map headers) { URL url; try { url = new URL(baseUrl, path); @@ -72,14 +78,21 @@ public class HttpClient { try { urlConnection = (HttpURLConnection) url.openConnection(); urlConnection.setRequestMethod(method); + if (headers != null) { + for (Map.Entry headerEntry : headers.entrySet()) { + urlConnection.setRequestProperty(headerEntry.getKey(), headerEntry.getValue()); + } + } urlConnection.connect(); } catch (IOException e) { throw new ElasticSearchException("", e); } int errorCode = -1; + Map> respHeaders = null; try { errorCode = urlConnection.getResponseCode(); + respHeaders = urlConnection.getHeaderFields(); InputStream inputStream = urlConnection.getInputStream(); String body = null; try { @@ -87,7 +100,7 @@ public class HttpClient { } catch (IOException e1) { throw new ElasticSearchException("problem reading error stream", e1); } - return new HttpClientResponse(body, errorCode, null); + return new HttpClientResponse(body, errorCode, respHeaders, null); } catch (IOException e) { InputStream errStream = urlConnection.getErrorStream(); String body = null; @@ -96,8 +109,7 @@ public class HttpClient { } catch (IOException e1) { throw new ElasticSearchException("problem reading error stream", e1); } - - return new HttpClientResponse(body, errorCode, e); + return new HttpClientResponse(body, errorCode, respHeaders, e); } finally { urlConnection.disconnect(); } diff --git a/src/test/java/org/elasticsearch/test/integration/rest/helper/HttpClientResponse.java b/src/test/java/org/elasticsearch/test/integration/rest/helper/HttpClientResponse.java index b84a16c1295..9f4ef9fbe7f 100644 --- a/src/test/java/org/elasticsearch/test/integration/rest/helper/HttpClientResponse.java +++ b/src/test/java/org/elasticsearch/test/integration/rest/helper/HttpClientResponse.java @@ -18,15 +18,20 @@ */ package org.elasticsearch.test.integration.rest.helper; +import java.util.List; +import java.util.Map; +import java.util.HashMap; public class HttpClientResponse { private final String response; private final int errorCode; + private Map> headers; private final Throwable e; - public HttpClientResponse(String response, int errorCode, Throwable e) { + public HttpClientResponse(String response, int errorCode, Map> headers, Throwable e) { this.response = response; this.errorCode = errorCode; + this.headers = headers; this.e = e; } @@ -41,4 +46,19 @@ public class HttpClientResponse { public Throwable cause() { return e; } + + public Map> getHeaders() { + return headers; + } + + public String getHeader(String name) { + if (headers == null) { + return null; + } + List vals = headers.get(name); + if (vals == null || vals.size() == 0) { + return null; + } + return vals.iterator().next(); + } } diff --git a/src/test/resources/org/elasticsearch/test/integration/responseheader/responseheader-plugin/es-plugin.properties b/src/test/resources/org/elasticsearch/test/integration/responseheader/responseheader-plugin/es-plugin.properties new file mode 100644 index 00000000000..e45c52468f7 --- /dev/null +++ b/src/test/resources/org/elasticsearch/test/integration/responseheader/responseheader-plugin/es-plugin.properties @@ -0,0 +1,20 @@ +################################################################ +# Licensed to ElasticSearch and Shay Banon 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. +################################################################ +plugin=org.elasticsearch.test.integration.plugin.responseheader.TestResponseHeaderPlugin +