mirror of https://github.com/apache/lucene.git
SOLR-6043: Add ability to set http headers in solr response
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1593423 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
564c221c95
commit
050b0b8af9
|
@ -145,6 +145,11 @@ Bug Fixes
|
|||
* SOLR-6039: fixed debug output when no results in response
|
||||
(Tomás Fernández Löbbe, hossman)
|
||||
|
||||
New Features
|
||||
----------------------
|
||||
|
||||
* SOLR-6043: Add ability to set http headers in solr response
|
||||
(Tomás Fernández Löbbe via Ryan Ernst)
|
||||
|
||||
Other Changes
|
||||
---------------------
|
||||
|
|
|
@ -17,13 +17,19 @@
|
|||
|
||||
package org.apache.solr.response;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.common.util.SimpleOrderedMap;
|
||||
import org.apache.solr.search.ReturnFields;
|
||||
import org.apache.solr.search.SolrReturnFields;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* <code>SolrQueryResponse</code> is used by a query handler to return
|
||||
* the response to a query request.
|
||||
|
@ -77,6 +83,15 @@ public class SolrQueryResponse {
|
|||
protected NamedList<Object> toLog = new SimpleOrderedMap<>();
|
||||
|
||||
protected ReturnFields returnFields;
|
||||
|
||||
/**
|
||||
* Container for storing HTTP headers. Internal Solr components can add headers to
|
||||
* this SolrQueryResponse through the methods: {@link #addHttpHeader(String, String)}
|
||||
* and {@link #setHttpHeader(String, String)}, or remove existing ones through
|
||||
* {@link #removeHttpHeader(String)} and {@link #removeHttpHeaders(String)}.
|
||||
* All these headers are going to be added to the HTTP response.
|
||||
*/
|
||||
private final NamedList<String> headers = new SimpleOrderedMap<>();
|
||||
|
||||
// error if this is set...
|
||||
protected Exception err;
|
||||
|
@ -245,4 +260,110 @@ public class SolrQueryResponse {
|
|||
public boolean isHttpCaching() {
|
||||
return this.httpCaching;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Sets a response header with the given name and value. This header
|
||||
* will be included in the HTTP response
|
||||
* If the header had already been set, the new value overwrites the
|
||||
* previous ones (all of them if there are multiple for the same name).
|
||||
*
|
||||
* @param name the name of the header
|
||||
* @param value the header value If it contains octet string,
|
||||
* it should be encoded according to RFC 2047
|
||||
* (http://www.ietf.org/rfc/rfc2047.txt)
|
||||
*
|
||||
* @see #addHttpHeader
|
||||
* @see HttpServletResponse#setHeader
|
||||
*/
|
||||
public void setHttpHeader(String name, String value) {
|
||||
headers.removeAll(name);
|
||||
headers.add(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a response header with the given name and value. This header
|
||||
* will be included in the HTTP response
|
||||
* This method allows response headers to have multiple values.
|
||||
*
|
||||
* @param name the name of the header
|
||||
* @param value the additional header value If it contains
|
||||
* octet string, it should be encoded
|
||||
* according to RFC 2047
|
||||
* (http://www.ietf.org/rfc/rfc2047.txt)
|
||||
*
|
||||
* @see #setHttpHeader
|
||||
* @see HttpServletResponse#addHeader
|
||||
*/
|
||||
public void addHttpHeader(String name, String value) {
|
||||
headers.add(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the response header with the given name.
|
||||
*
|
||||
* <p>If a response header with the given name exists and contains
|
||||
* multiple values, the value that was added first will be returned.</p>
|
||||
*
|
||||
* <p>NOTE: this runs in linear time (it scans starting at the
|
||||
* beginning of the list until it finds the first pair with
|
||||
* the specified name).</p>
|
||||
*
|
||||
* @param name the name of the response header whose value to return
|
||||
* @return the value of the response header with the given name,
|
||||
* or <tt>null</tt> if no header with the given name has been set
|
||||
* on this response
|
||||
*/
|
||||
public String getHttpHeader(String name) {
|
||||
return headers.get(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the values of the response header with the given name.
|
||||
*
|
||||
* @param name the name of the response header whose values to return
|
||||
*
|
||||
* @return a (possibly empty) <code>Collection</code> of the values
|
||||
* of the response header with the given name
|
||||
*
|
||||
*/
|
||||
public Collection<String> getHttpHeaders(String name) {
|
||||
return headers.getAll(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a previously added header with the given name (only
|
||||
* the first one if multiple are present for the same name)
|
||||
*
|
||||
* <p>NOTE: this runs in linear time (it scans starting at the
|
||||
* beginning of the list until it finds the first pair with
|
||||
* the specified name).</p>
|
||||
*
|
||||
* @param name the name of the response header to remove
|
||||
* @return the value of the removed entry or <tt>null</tt> if no
|
||||
* value is found for the given header name
|
||||
*/
|
||||
public String removeHttpHeader(String name) {
|
||||
return headers.remove(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all previously added headers with the given name.
|
||||
*
|
||||
* @param name the name of the response headers to remove
|
||||
* @return a <code>Collection</code> with all the values
|
||||
* of the removed entries. It returns <code>null</code> if no
|
||||
* entries are found for the given name
|
||||
*/
|
||||
public Collection<String> removeHttpHeaders(String name) {
|
||||
return headers.removeAll(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new iterator of response headers
|
||||
* @return a new Iterator instance for the response headers
|
||||
*/
|
||||
public Iterator<Entry<String, String>> httpHeaders() {
|
||||
return headers.iterator();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -419,16 +419,11 @@ public class SolrDispatchFilter extends BaseSolrFilter {
|
|||
SolrRequestInfo.setRequestInfo(new SolrRequestInfo(solrReq, solrRsp));
|
||||
this.execute( req, handler, solrReq, solrRsp );
|
||||
HttpCacheHeaderUtil.checkHttpCachingVeto(solrRsp, resp, reqMethod);
|
||||
// add info to http headers
|
||||
//TODO: See SOLR-232 and SOLR-267.
|
||||
/*try {
|
||||
NamedList solrRspHeader = solrRsp.getResponseHeader();
|
||||
for (int i=0; i<solrRspHeader.size(); i++) {
|
||||
((javax.servlet.http.HttpServletResponse) response).addHeader(("Solr-" + solrRspHeader.getName(i)), String.valueOf(solrRspHeader.getVal(i)));
|
||||
}
|
||||
} catch (ClassCastException cce) {
|
||||
log.log(Level.WARNING, "exception adding response header log information", cce);
|
||||
}*/
|
||||
Iterator<Entry<String, String>> headers = solrRsp.httpHeaders();
|
||||
while (headers.hasNext()) {
|
||||
Entry<String, String> entry = headers.next();
|
||||
resp.addHeader(entry.getKey(), entry.getValue());
|
||||
}
|
||||
QueryResponseWriter responseWriter = core.getQueryResponseWriter(solrReq);
|
||||
writeResponse(solrRsp, response, responseWriter, solrReq, reqMethod);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" ?>
|
||||
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
contributor license agreements. See the NOTICE file distributed with
|
||||
this work for additional information regarding copyright ownership.
|
||||
The ASF 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.
|
||||
-->
|
||||
|
||||
<config>
|
||||
<luceneMatchVersion>${tests.luceneMatchVersion:LUCENE_CURRENT}</luceneMatchVersion>
|
||||
<dataDir>${solr.data.dir:}</dataDir>
|
||||
<xi:include href="solrconfig.snippet.randomindexconfig.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
|
||||
<directoryFactory name="DirectoryFactory" class="${solr.directoryFactory:solr.RAMDirectoryFactory}"/>
|
||||
<searchComponent name="componentThatAddsHeader" class="org.apache.solr.servlet.ResponseHeaderTest$ComponentThatAddsHeader"/>
|
||||
<requestHandler name="/withHeaders" class="solr.StandardRequestHandler">
|
||||
<arr name="first-components">
|
||||
<str>componentThatAddsHeader</str>
|
||||
</arr>
|
||||
</requestHandler>
|
||||
<requestHandler name="/update" class="solr.UpdateRequestHandler" />
|
||||
</config>
|
|
@ -0,0 +1,231 @@
|
|||
package org.apache.solr.servlet;
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF 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.
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.solr.SolrJettyTestBase;
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.client.solrj.SolrServerException;
|
||||
import org.apache.solr.client.solrj.impl.HttpSolrServer;
|
||||
import org.apache.solr.handler.component.ResponseBuilder;
|
||||
import org.apache.solr.handler.component.SearchComponent;
|
||||
import org.apache.solr.response.SolrQueryResponse;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
public class ResponseHeaderTest extends SolrJettyTestBase {
|
||||
|
||||
private static File solrHomeDirectory;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeTest() throws Exception {
|
||||
solrHomeDirectory = createTempDir();
|
||||
setupJettyTestHome(solrHomeDirectory, "collection1");
|
||||
String top = SolrTestCaseJ4.TEST_HOME() + "/collection1/conf";
|
||||
FileUtils.copyFile(new File(top, "solrconfig-headers.xml"), new File(solrHomeDirectory + "/collection1/conf", "solrconfig.xml"));
|
||||
createJetty(solrHomeDirectory.getAbsolutePath(), null, null);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterTest() throws Exception {
|
||||
cleanUpJettyHome(solrHomeDirectory);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpResponse() throws SolrServerException, IOException {
|
||||
HttpSolrServer client = (HttpSolrServer)getSolrServer();
|
||||
HttpClient httpClient = client.getHttpClient();
|
||||
URI uri = URI.create(client.getBaseURL() + "/withHeaders?q=*:*");
|
||||
HttpGet httpGet = new HttpGet(uri);
|
||||
HttpResponse response = httpClient.execute(httpGet);
|
||||
Header[] headers = response.getAllHeaders();
|
||||
boolean containsWarningHeader = false;
|
||||
for (Header header:headers) {
|
||||
if ("Warning".equals(header.getName())) {
|
||||
containsWarningHeader = true;
|
||||
assertEquals("This is a test warning", header.getValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertTrue("Expected header not found", containsWarningHeader);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddHttpHeader() {
|
||||
SolrQueryResponse response = new SolrQueryResponse();
|
||||
Iterator<Entry<String, String>> it = response.httpHeaders();
|
||||
assertFalse(it.hasNext());
|
||||
|
||||
response.addHttpHeader("key1", "value1");
|
||||
it = response.httpHeaders();
|
||||
assertTrue(it.hasNext());
|
||||
Entry<String, String> entry = it.next();
|
||||
assertEquals("key1", entry.getKey());
|
||||
assertEquals("value1", entry.getValue());
|
||||
assertFalse(it.hasNext());
|
||||
|
||||
response.addHttpHeader("key1", "value2");
|
||||
it = response.httpHeaders();
|
||||
assertTrue(it.hasNext());
|
||||
entry = it.next();
|
||||
assertEquals("key1", entry.getKey());
|
||||
assertEquals("value1", entry.getValue());
|
||||
assertTrue(it.hasNext());
|
||||
entry = it.next();
|
||||
assertEquals("key1", entry.getKey());
|
||||
assertEquals("value2", entry.getValue());
|
||||
assertFalse(it.hasNext());
|
||||
|
||||
response.addHttpHeader("key2", "value2");
|
||||
it = response.httpHeaders();
|
||||
assertTrue(it.hasNext());
|
||||
entry = it.next();
|
||||
assertEquals("key1", entry.getKey());
|
||||
assertEquals("value1", entry.getValue());
|
||||
assertTrue(it.hasNext());
|
||||
entry = it.next();
|
||||
assertEquals("key1", entry.getKey());
|
||||
assertEquals("value2", entry.getValue());
|
||||
assertTrue(it.hasNext());
|
||||
entry = it.next();
|
||||
assertEquals("key2", entry.getKey());
|
||||
assertEquals("value2", entry.getValue());
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetHttpHeader() {
|
||||
SolrQueryResponse response = new SolrQueryResponse();
|
||||
Iterator<Entry<String, String>> it = response.httpHeaders();
|
||||
assertFalse(it.hasNext());
|
||||
|
||||
response.setHttpHeader("key1", "value1");
|
||||
it = response.httpHeaders();
|
||||
assertTrue(it.hasNext());
|
||||
Entry<String, String> entry = it.next();
|
||||
assertEquals("key1", entry.getKey());
|
||||
assertEquals("value1", entry.getValue());
|
||||
assertFalse(it.hasNext());
|
||||
|
||||
response.setHttpHeader("key1", "value2");
|
||||
it = response.httpHeaders();
|
||||
assertTrue(it.hasNext());
|
||||
entry = it.next();
|
||||
assertEquals("key1", entry.getKey());
|
||||
assertEquals("value2", entry.getValue());
|
||||
assertFalse(it.hasNext());
|
||||
|
||||
response.addHttpHeader("key1", "value3");
|
||||
response.setHttpHeader("key1", "value4");
|
||||
it = response.httpHeaders();
|
||||
assertTrue(it.hasNext());
|
||||
entry = it.next();
|
||||
assertEquals("key1", entry.getKey());
|
||||
assertEquals("value4", entry.getValue());
|
||||
assertFalse(it.hasNext());
|
||||
|
||||
response.setHttpHeader("key2", "value5");
|
||||
it = response.httpHeaders();
|
||||
assertTrue(it.hasNext());
|
||||
entry = it.next();
|
||||
assertEquals("key1", entry.getKey());
|
||||
assertEquals("value4", entry.getValue());
|
||||
assertTrue(it.hasNext());
|
||||
entry = it.next();
|
||||
assertEquals("key2", entry.getKey());
|
||||
assertEquals("value5", entry.getValue());
|
||||
assertFalse(it.hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveHttpHeader() {
|
||||
SolrQueryResponse response = new SolrQueryResponse();
|
||||
Iterator<Entry<String, String>> it = response.httpHeaders();
|
||||
assertFalse(it.hasNext());
|
||||
response.addHttpHeader("key1", "value1");
|
||||
assertTrue(response.httpHeaders().hasNext());
|
||||
assertEquals("value1", response.removeHttpHeader("key1"));
|
||||
assertFalse(response.httpHeaders().hasNext());
|
||||
|
||||
response.addHttpHeader("key1", "value2");
|
||||
response.addHttpHeader("key1", "value3");
|
||||
response.addHttpHeader("key2", "value4");
|
||||
assertTrue(response.httpHeaders().hasNext());
|
||||
assertEquals("value2", response.removeHttpHeader("key1"));
|
||||
assertEquals("value3", response.httpHeaders().next().getValue());
|
||||
assertEquals("value3", response.removeHttpHeader("key1"));
|
||||
assertNull(response.removeHttpHeader("key1"));
|
||||
assertEquals("key2", response.httpHeaders().next().getKey());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveHttpHeaders() {
|
||||
SolrQueryResponse response = new SolrQueryResponse();
|
||||
Iterator<Entry<String, String>> it = response.httpHeaders();
|
||||
assertFalse(it.hasNext());
|
||||
response.addHttpHeader("key1", "value1");
|
||||
assertTrue(response.httpHeaders().hasNext());
|
||||
assertEquals(Arrays.asList("value1"), response.removeHttpHeaders("key1"));
|
||||
assertFalse(response.httpHeaders().hasNext());
|
||||
|
||||
response.addHttpHeader("key1", "value2");
|
||||
response.addHttpHeader("key1", "value3");
|
||||
response.addHttpHeader("key2", "value4");
|
||||
assertTrue(response.httpHeaders().hasNext());
|
||||
assertEquals(Arrays.asList(new String[]{"value2", "value3"}), response.removeHttpHeaders("key1"));
|
||||
assertNull(response.removeHttpHeaders("key1"));
|
||||
assertEquals("key2", response.httpHeaders().next().getKey());
|
||||
}
|
||||
|
||||
public static class ComponentThatAddsHeader extends SearchComponent {
|
||||
|
||||
@Override
|
||||
public void prepare(ResponseBuilder rb) throws IOException {
|
||||
rb.rsp.addHttpHeader("Warning", "This is a test warning");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(ResponseBuilder rb) throws IOException {}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSource() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue