SOLR-10748: Make stream.body configurable and disabled by default

This commit is contained in:
Jan Høydahl 2017-07-06 14:56:58 +02:00
parent 112bdda47e
commit 80b1430a3e
22 changed files with 221 additions and 83 deletions

View File

@ -77,6 +77,8 @@ Other Changes
* SOLR-10957: Changed SolrCoreParser.init to use the resource loader from getSchema() * SOLR-10957: Changed SolrCoreParser.init to use the resource loader from getSchema()
instead of the resource loader from getCore(). (Christine Poerschke) instead of the resource loader from getCore(). (Christine Poerschke)
* SOLR-10748: Make stream.body configurable and disabled by default (janhoy)
================== 7.0.0 ================== ================== 7.0.0 ==================
Versions of Major Components Versions of Major Components

View File

@ -18,6 +18,9 @@
<schemaFactory class="ClassicIndexSchemaFactory" /> <schemaFactory class="ClassicIndexSchemaFactory" />
<requestDispatcher>
<requestParsers enableStreamBody="true" />
</requestDispatcher>
<!-- Query parser used to rerank top docs with a provided model --> <!-- Query parser used to rerank top docs with a provided model -->
<queryParser name="ltr" <queryParser name="ltr"

View File

@ -18,7 +18,10 @@
<schemaFactory class="ClassicIndexSchemaFactory" /> <schemaFactory class="ClassicIndexSchemaFactory" />
<requestDispatcher>
<requestParsers enableStreamBody="true" />
</requestDispatcher>
<!-- Query parser used to rerank top docs with a provided model --> <!-- Query parser used to rerank top docs with a provided model -->
<queryParser name="ltr" class="org.apache.solr.ltr.search.LTRQParserPlugin" > <queryParser name="ltr" class="org.apache.solr.ltr.search.LTRQParserPlugin" >
<int name="threadModule.totalPoolThreads">10</int> <!-- Maximum threads to use for all queries --> <int name="threadModule.totalPoolThreads">10</int> <!-- Maximum threads to use for all queries -->

View File

@ -18,7 +18,10 @@
<schemaFactory class="ClassicIndexSchemaFactory" /> <schemaFactory class="ClassicIndexSchemaFactory" />
<requestDispatcher>
<requestParsers enableStreamBody="true" />
</requestDispatcher>
<!-- Query parser used to rerank top docs with a provided model --> <!-- Query parser used to rerank top docs with a provided model -->
<queryParser name="ltr" class="org.apache.solr.ltr.search.LTRQParserPlugin" /> <queryParser name="ltr" class="org.apache.solr.ltr.search.LTRQParserPlugin" />

View File

@ -123,6 +123,7 @@ public class SolrConfig extends Config implements MapSerializable {
private int formUploadLimitKB; private int formUploadLimitKB;
private boolean enableRemoteStreams; private boolean enableRemoteStreams;
private boolean enableStreamBody;
private boolean handleSelect; private boolean handleSelect;
@ -308,6 +309,9 @@ public class SolrConfig extends Config implements MapSerializable {
enableRemoteStreams = getBool( enableRemoteStreams = getBool(
"requestDispatcher/requestParsers/@enableRemoteStreaming", false); "requestDispatcher/requestParsers/@enableRemoteStreaming", false);
enableStreamBody = getBool(
"requestDispatcher/requestParsers/@enableStreamBody", false);
handleSelect = getBool( handleSelect = getBool(
"requestDispatcher/@handleSelect", !luceneMatchVersion.onOrAfter(Version.LUCENE_7_0_0)); "requestDispatcher/@handleSelect", !luceneMatchVersion.onOrAfter(Version.LUCENE_7_0_0));
@ -784,6 +788,10 @@ public class SolrConfig extends Config implements MapSerializable {
return enableRemoteStreams; return enableRemoteStreams;
} }
public boolean isEnableStreamBody() {
return enableStreamBody;
}
@Override @Override
public int getInt(String path) { public int getInt(String path) {
return getInt(path, 0); return getInt(path, 0);

View File

@ -84,6 +84,7 @@ public class SolrRequestParsers
private final HashMap<String, SolrRequestParser> parsers = private final HashMap<String, SolrRequestParser> parsers =
new HashMap<>(); new HashMap<>();
private final boolean enableRemoteStreams; private final boolean enableRemoteStreams;
private final boolean enableStreamBody;
private StandardRequestParser standard; private StandardRequestParser standard;
private boolean handleSelect = true; private boolean handleSelect = true;
private boolean addHttpRequestToContext; private boolean addHttpRequestToContext;
@ -101,8 +102,9 @@ public class SolrRequestParsers
final int multipartUploadLimitKB, formUploadLimitKB; final int multipartUploadLimitKB, formUploadLimitKB;
if( globalConfig == null ) { if( globalConfig == null ) {
multipartUploadLimitKB = formUploadLimitKB = Integer.MAX_VALUE; multipartUploadLimitKB = formUploadLimitKB = Integer.MAX_VALUE;
enableRemoteStreams = true; enableRemoteStreams = false;
handleSelect = true; enableStreamBody = false;
handleSelect = false;
addHttpRequestToContext = false; addHttpRequestToContext = false;
} else { } else {
multipartUploadLimitKB = globalConfig.getMultipartUploadLimitKB(); multipartUploadLimitKB = globalConfig.getMultipartUploadLimitKB();
@ -110,6 +112,7 @@ public class SolrRequestParsers
formUploadLimitKB = globalConfig.getFormUploadLimitKB(); formUploadLimitKB = globalConfig.getFormUploadLimitKB();
enableRemoteStreams = globalConfig.isEnableRemoteStreams(); enableRemoteStreams = globalConfig.isEnableRemoteStreams();
enableStreamBody = globalConfig.isEnableStreamBody();
// Let this filter take care of /select?xxx format // Let this filter take care of /select?xxx format
handleSelect = globalConfig.isHandleSelect(); handleSelect = globalConfig.isHandleSelect();
@ -121,9 +124,10 @@ public class SolrRequestParsers
private SolrRequestParsers() { private SolrRequestParsers() {
enableRemoteStreams = false; enableRemoteStreams = false;
enableStreamBody = false;
handleSelect = false; handleSelect = false;
addHttpRequestToContext = false; addHttpRequestToContext = false;
init(2048, 2048); init(Integer.MAX_VALUE, Integer.MAX_VALUE);
} }
private void init( int multipartUploadLimitKB, int formUploadLimitKB) { private void init( int multipartUploadLimitKB, int formUploadLimitKB) {
@ -202,7 +206,7 @@ public class SolrRequestParsers
strs = params.getParams( CommonParams.STREAM_FILE ); strs = params.getParams( CommonParams.STREAM_FILE );
if( strs != null ) { if( strs != null ) {
if( !enableRemoteStreams ) { if( !enableRemoteStreams ) {
throw new SolrException( ErrorCode.BAD_REQUEST, "Remote Streaming is disabled." ); throw new SolrException( ErrorCode.BAD_REQUEST, "Remote Streaming is disabled. See http://lucene.apache.org/solr/guide/requestdispatcher-in-solrconfig.html for help" );
} }
for( final String file : strs ) { for( final String file : strs ) {
ContentStreamBase stream = new ContentStreamBase.FileStream( new File(file) ); ContentStreamBase stream = new ContentStreamBase.FileStream( new File(file) );
@ -216,6 +220,9 @@ public class SolrRequestParsers
// Check for streams in the request parameters // Check for streams in the request parameters
strs = params.getParams( CommonParams.STREAM_BODY ); strs = params.getParams( CommonParams.STREAM_BODY );
if( strs != null ) { if( strs != null ) {
if( !enableStreamBody ) {
throw new SolrException( ErrorCode.BAD_REQUEST, "Stream Body is disabled. See http://lucene.apache.org/solr/guide/requestdispatcher-in-solrconfig.html for help" );
}
for( final String body : strs ) { for( final String body : strs ) {
ContentStreamBase stream = new ContentStreamBase.StringStream( body ); ContentStreamBase stream = new ContentStreamBase.StringStream( body );
if( contentType != null ) { if( contentType != null ) {

View File

@ -65,7 +65,8 @@
"requestParsers":{ "requestParsers":{
"multipartUploadLimitInKB":0, "multipartUploadLimitInKB":0,
"formdataUploadLimitInKB":0, "formdataUploadLimitInKB":0,
"enableRemoteStreaming":0, "enableRemoteStreaming":10,
"enableStreamBody":10,
"addHttpRequestToContext":0}}, "addHttpRequestToContext":0}},
"peerSync":{ "peerSync":{
"useRangeVersions":11 "useRangeVersions":11

View File

@ -29,6 +29,11 @@
<codecFactory class="solr.SchemaCodecFactory"/> <codecFactory class="solr.SchemaCodecFactory"/>
<requestDispatcher>
<!-- Tests rely on stream.body -->
<requestParsers enableStreamBody="true" />
</requestDispatcher>
<query> <query>
<filterCache <filterCache
enabled="${filterCache.enabled:false}" enabled="${filterCache.enabled:false}"

View File

@ -35,6 +35,10 @@
</updateLog> </updateLog>
</updateHandler> </updateHandler>
<requestDispatcher>
<requestParsers enableStreamBody="true" />
</requestDispatcher>
<requestHandler name="/select" class="solr.SearchHandler"> <requestHandler name="/select" class="solr.SearchHandler">
<bool name="httpCaching">true</bool> <bool name="httpCaching">true</bool>
</requestHandler> </requestHandler>

View File

@ -31,6 +31,9 @@
<str name="solr.hdfs.blockcache.global">${solr.hdfs.blockcache.global:true}</str> <str name="solr.hdfs.blockcache.global">${solr.hdfs.blockcache.global:true}</str>
</directoryFactory> </directoryFactory>
<schemaFactory class="ClassicIndexSchemaFactory"/> <schemaFactory class="ClassicIndexSchemaFactory"/>
<requestDispatcher>
<requestParsers enableStreamBody="true" />
</requestDispatcher>
<dataDir>${solr.data.dir:}</dataDir> <dataDir>${solr.data.dir:}</dataDir>

View File

@ -457,7 +457,7 @@
</searchComponent> </searchComponent>
<requestDispatcher> <requestDispatcher>
<requestParsers enableRemoteStreaming="true" multipartUploadLimitInKB="-1" /> <requestParsers enableRemoteStreaming="true" enableStreamBody="true" multipartUploadLimitInKB="-1" />
<httpCaching lastModifiedFrom="openTime" etagSeed="Solr" never304="false"> <httpCaching lastModifiedFrom="openTime" etagSeed="Solr" never304="false">
<cacheControl>max-age=30, public</cacheControl> <cacheControl>max-age=30, public</cacheControl>
</httpCaching> </httpCaching>

View File

@ -38,7 +38,9 @@
<boolTofilterOptimizer enabled="true" cacheSize="32" threshold=".05"/> <boolTofilterOptimizer enabled="true" cacheSize="32" threshold=".05"/>
</query> </query>
<requestDispatcher/> <requestDispatcher>
<requestParsers enableStreamBody="true" />
</requestDispatcher>
<requestHandler name="/select" class="solr.SearchHandler" /> <requestHandler name="/select" class="solr.SearchHandler" />
<requestHandler name="/crazy_custom_qt" class="solr.SearchHandler"> <requestHandler name="/crazy_custom_qt" class="solr.SearchHandler">

View File

@ -37,6 +37,7 @@ import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult; import java.nio.file.FileVisitResult;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
@ -47,10 +48,10 @@ import java.util.LinkedList;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.nio.file.SimpleFileVisitor;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream; import java.util.zip.ZipOutputStream;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.client.HttpClient; import org.apache.http.client.HttpClient;
@ -61,27 +62,22 @@ import org.apache.http.util.EntityUtils;
import org.apache.lucene.util.TestUtil; import org.apache.lucene.util.TestUtil;
import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.embedded.JettySolrRunner; import org.apache.solr.client.solrj.embedded.JettySolrRunner;
import org.apache.solr.client.solrj.impl.CloudSolrClient; import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.client.solrj.impl.HttpClientUtil; import org.apache.solr.client.solrj.impl.HttpClientUtil;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.request.ConfigSetAdminRequest; import org.apache.solr.client.solrj.request.ConfigSetAdminRequest;
import org.apache.solr.client.solrj.request.ConfigSetAdminRequest.Create; import org.apache.solr.client.solrj.request.ConfigSetAdminRequest.Create;
import org.apache.solr.client.solrj.request.ConfigSetAdminRequest.Delete; import org.apache.solr.client.solrj.request.ConfigSetAdminRequest.Delete;
import org.apache.solr.client.solrj.request.QueryRequest; import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.client.solrj.response.CollectionAdminResponse; import org.apache.solr.client.solrj.response.CollectionAdminResponse;
import org.apache.solr.client.solrj.response.ConfigSetAdminResponse; import org.apache.solr.client.solrj.response.ConfigSetAdminResponse;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.SolrInputDocument; import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.cloud.SolrZkClient; import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.common.cloud.ZkConfigManager; import org.apache.solr.common.cloud.ZkConfigManager;
import org.apache.solr.common.cloud.ZkStateReader; import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.params.CollectionParams.CollectionAction; import org.apache.solr.common.params.CollectionParams.CollectionAction;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.ConfigSetParams; import org.apache.solr.common.params.ConfigSetParams;
import org.apache.solr.common.params.ConfigSetParams.ConfigSetAction; import org.apache.solr.common.params.ConfigSetParams.ConfigSetAction;
import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.params.ModifiableSolrParams;
@ -104,8 +100,6 @@ import org.noggit.ObjectBuilder;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableMap;
/** /**
* Simple ConfigSets API tests on user errors and simple success cases. * Simple ConfigSets API tests on user errors and simple success cases.
*/ */
@ -331,7 +325,6 @@ public class TestConfigSetsAPI extends SolrTestCaseJ4 {
uploadConfigSet("regular", suffix, null, null); uploadConfigSet("regular", suffix, null, null);
// try to create a collection with the uploaded configset // try to create a collection with the uploaded configset
createCollection("newcollection", "regular" + suffix, 1, 1, solrCluster.getSolrClient()); createCollection("newcollection", "regular" + suffix, 1, 1, solrCluster.getSolrClient());
xsltRequest("newcollection");
} }
@Test @Test
@ -506,35 +499,6 @@ public class TestConfigSetsAPI extends SolrTestCaseJ4 {
zout.close(); zout.close();
} }
} }
private void xsltRequest(String collection) throws SolrServerException, IOException {
String baseUrl = solrCluster.getJettySolrRunners().get(0).getBaseUrl().toString();
try (HttpSolrClient client = getHttpSolrClient(baseUrl + "/" + collection)) {
String xml =
"<random>" +
" <document>" +
" <node name=\"id\" value=\"12345\"/>" +
" <node name=\"name\" value=\"kitten\"/>" +
" <node name=\"text\" enhance=\"3\" value=\"some other day\"/>" +
" <node name=\"title\" enhance=\"4\" value=\"A story\"/>" +
" <node name=\"timestamp\" enhance=\"5\" value=\"2011-07-01T10:31:57.140Z\"/>" +
" </document>" +
"</random>";
SolrQuery query = new SolrQuery();
query.setQuery( "*:*" );//for anything
query.add("qt","/update");
query.add(CommonParams.TR, "xsl-update-handler-test.xsl");
query.add("stream.body", xml);
query.add("commit", "true");
try {
client.query(query);
fail("This should've returned a 401.");
} catch (SolrException ex) {
assertEquals(ErrorCode.UNAUTHORIZED.code, ex.code());
}
}
}
public void scriptRequest(String collection) throws SolrServerException, IOException { public void scriptRequest(String collection) throws SolrServerException, IOException {
SolrClient client = solrCluster.getSolrClient(); SolrClient client = solrCluster.getSolrClient();

View File

@ -53,6 +53,7 @@ public class TestConfigOverlay extends LuceneTestCase {
assertTrue(isEditableProp("requestDispatcher.requestParsers.multipartUploadLimitInKB", false, null)); assertTrue(isEditableProp("requestDispatcher.requestParsers.multipartUploadLimitInKB", false, null));
assertTrue(isEditableProp("requestDispatcher.requestParsers.formdataUploadLimitInKB", false, null)); assertTrue(isEditableProp("requestDispatcher.requestParsers.formdataUploadLimitInKB", false, null));
assertTrue(isEditableProp("requestDispatcher.requestParsers.enableRemoteStreaming", false, null)); assertTrue(isEditableProp("requestDispatcher.requestParsers.enableRemoteStreaming", false, null));
assertTrue(isEditableProp("requestDispatcher.requestParsers.enableStreamBody", false, null));
assertTrue(isEditableProp("requestDispatcher.requestParsers.addHttpRequestToContext", false, null)); assertTrue(isEditableProp("requestDispatcher.requestParsers.addHttpRequestToContext", false, null));
assertTrue(isEditableProp("requestDispatcher.handleSelect", false, null)); assertTrue(isEditableProp("requestDispatcher.handleSelect", false, null));

View File

@ -182,7 +182,8 @@ public class TestSolrConfigHandler extends RestTestBase {
log.info("going to send config command. path {} , payload: {}", uri, payload); log.info("going to send config command. path {} , payload: {}", uri, payload);
String response = harness.post(uri, json); String response = harness.post(uri, json);
Map map = (Map) ObjectBuilder.getVal(new JSONParser(new StringReader(response))); Map map = (Map) ObjectBuilder.getVal(new JSONParser(new StringReader(response)));
assertNull(response, map.get("errors")); assertNull(response, map.get("errorMessages"));
assertNull(response, map.get("errors")); // Will this ever be returned?
} }

View File

@ -20,15 +20,14 @@ import org.apache.commons.io.IOUtils;
import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.util.LuceneTestCase;
import org.apache.solr.SolrJettyTestBase; import org.apache.solr.SolrJettyTestBase;
import org.apache.solr.SolrTestCaseJ4.SuppressSSL; import org.apache.solr.SolrTestCaseJ4.SuppressSSL;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.HttpSolrClient; import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.SolrInputDocument;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
@ -120,30 +119,7 @@ public class TestRemoteStreaming extends SolrJettyTestBase {
assertSame(ErrorCode.BAD_REQUEST, ErrorCode.getErrorCode(se.code())); assertSame(ErrorCode.BAD_REQUEST, ErrorCode.getErrorCode(se.code()));
} }
} }
/** SOLR-3161
* Technically stream.body isn't remote streaming, but there wasn't a better place for this test method. */
@Test(expected = SolrException.class)
public void testQtUpdateFails() throws SolrServerException, IOException {
SolrQuery query = new SolrQuery();
query.setQuery( "*:*" );//for anything
query.add("echoHandler","true");
//sneaky sneaky
query.add("qt","/update");
query.add("stream.body","<delete><query>*:*</query></delete>");
QueryRequest queryRequest = new QueryRequest(query) {
@Override
public String getPath() { //don't let superclass substitute qt for the path
return "/select";
}
};
QueryResponse rsp = queryRequest.process(getSolrClient());
//!! should *fail* above for security purposes
String handler = (String) rsp.getHeader().get("handler");
System.out.println(handler);
}
/** Compose a url that if you get it, it will delete all the data. */ /** Compose a url that if you get it, it will delete all the data. */
private String makeDeleteAllUrl() throws UnsupportedEncodingException { private String makeDeleteAllUrl() throws UnsupportedEncodingException {
HttpSolrClient client = (HttpSolrClient) getSolrClient(); HttpSolrClient client = (HttpSolrClient) getSolrClient();

View File

@ -0,0 +1,138 @@
/*
* 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.
*/
package org.apache.solr.request;
import java.io.File;
import java.lang.invoke.MethodHandles;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.commons.io.FileUtils;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.request.QueryRequest;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.util.RestTestBase;
import org.apache.solr.util.RestTestHarness;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.restlet.ext.servlet.ServerServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.apache.solr.core.TestSolrConfigHandler.runConfigCommand;
public class TestStreamBody extends RestTestBase {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private static final String collection = "collection1";
private static final String confDir = collection + "/conf";
@Before
public void before() throws Exception {
File tmpSolrHome = createTempDir().toFile();
FileUtils.copyDirectory(new File(TEST_HOME()), tmpSolrHome.getAbsoluteFile());
final SortedMap<ServletHolder, String> extraServlets = new TreeMap<>();
final ServletHolder solrRestApi = new ServletHolder("SolrSchemaRestApi", ServerServlet.class);
solrRestApi.setInitParameter("org.restlet.application", "org.apache.solr.rest.SolrSchemaRestApi");
extraServlets.put(solrRestApi, "/schema/*"); // '/schema/*' matches '/schema', '/schema/', and '/schema/whatever...'
System.setProperty("managed.schema.mutable", "true");
System.setProperty("enable.update.log", "false");
createJettyAndHarness(tmpSolrHome.getAbsolutePath(), "solrconfig-minimal.xml", "schema-rest.xml",
"/solr", true, extraServlets);
if (random().nextBoolean()) {
log.info("These tests are run with V2 API");
restTestHarness.setServerProvider(() -> jetty.getBaseUrl().toString() + "/____v2/cores/" + DEFAULT_TEST_CORENAME);
}
}
@After
public void after() throws Exception {
if (jetty != null) {
jetty.stop();
jetty = null;
}
if (client != null) {
client.close();
client = null;
}
if (restTestHarness != null) {
restTestHarness.close();
restTestHarness = null;
}
}
// SOLR-3161
@Test
public void testQtUpdateFails() throws Exception {
enableStreamBody(true);
SolrQuery query = new SolrQuery();
query.setQuery( "*:*" );//for anything
query.add("echoHandler","true");
//sneaky sneaky
query.add("qt","/update");
query.add(CommonParams.STREAM_BODY,"<delete><query>*:*</query></delete>");
QueryRequest queryRequest = new QueryRequest(query) {
@Override
public String getPath() { //don't let superclass substitute qt for the path
return "/select";
}
};
try {
queryRequest.process(getSolrClient());
fail();
} catch (SolrException se) {
assertTrue(se.getMessage(), se.getMessage().contains("Bad contentType for search handler :text/xml"));
}
}
// Tests that stream.body is disabled by default, and can be edited through Config API
@Test
public void testStreamBodyDefaultAndConfigApi() throws Exception {
SolrQuery query = new SolrQuery();
query.add(CommonParams.STREAM_BODY,"<delete><query>*:*</query></delete>");
query.add("commit","true");
QueryRequest queryRequest = new QueryRequest(query) {
@Override
public String getPath() { //don't let superclass substitute qt for the path
return "/update";
}
};
try {
queryRequest.process(getSolrClient());
fail();
} catch (SolrException se) {
assertTrue(se.getMessage(), se.getMessage().contains("Stream Body is disabled"));
}
enableStreamBody(true);
queryRequest.process(getSolrClient());
}
// Enables/disables stream.body through Config API
private void enableStreamBody(boolean enable) throws Exception {
RestTestHarness harness = restTestHarness;
String payload = "{ 'set-property' : { 'requestDispatcher.requestParsers.enableStreamBody':" + enable + "} }";
runConfigCommand(harness, "/config?wt=json", payload);
}
}

View File

@ -20,6 +20,7 @@ import org.apache.lucene.util.LuceneTestCase;
import org.apache.solr.JSONTestUtil; import org.apache.solr.JSONTestUtil;
import org.apache.solr.SolrTestCaseHS; import org.apache.solr.SolrTestCaseHS;
import org.apache.solr.common.params.CommonParams;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
@ -181,33 +182,33 @@ public class TestJsonRequest extends SolrTestCaseHS {
// //
// with body // with body
// //
client.testJQ(params("stream.body", "{query:'cat_s:A'}", "stream.contentType", "application/json") client.testJQ(params(CommonParams.STREAM_BODY, "{query:'cat_s:A'}", "stream.contentType", "application/json")
, "response/numFound==2" , "response/numFound==2"
); );
// test body in conjunction with query params // test body in conjunction with query params
client.testJQ(params("stream.body", "{query:'cat_s:A'}", "stream.contentType", "application/json", "json.filter", "'where_s:NY'") client.testJQ(params(CommonParams.STREAM_BODY, "{query:'cat_s:A'}", "stream.contentType", "application/json", "json.filter", "'where_s:NY'")
, "response/numFound==1" , "response/numFound==1"
); );
// test that json body in params come "after" (will overwrite) // test that json body in params come "after" (will overwrite)
client.testJQ(params("stream.body", "{query:'*:*', filter:'where_s:NY'}", "stream.contentType", "application/json", "json","{query:'cat_s:A'}") client.testJQ(params(CommonParams.STREAM_BODY, "{query:'*:*', filter:'where_s:NY'}", "stream.contentType", "application/json", "json","{query:'cat_s:A'}")
, "response/numFound==1" , "response/numFound==1"
); );
// test that json.x params come after body // test that json.x params come after body
client.testJQ(params("stream.body", "{query:'*:*', filter:'where_s:NY'}", "stream.contentType", "application/json", "json.query","'cat_s:A'") client.testJQ(params(CommonParams.STREAM_BODY, "{query:'*:*', filter:'where_s:NY'}", "stream.contentType", "application/json", "json.query","'cat_s:A'")
, "response/numFound==1" , "response/numFound==1"
); );
// test facet with json body // test facet with json body
client.testJQ(params("stream.body", "{query:'*:*', facet:{x:'unique(where_s)'}}", "stream.contentType", "application/json") client.testJQ(params(CommonParams.STREAM_BODY, "{query:'*:*', facet:{x:'unique(where_s)'}}", "stream.contentType", "application/json")
, "facets=={count:6,x:2}" , "facets=={count:6,x:2}"
); );
// test facet with json body, insert additional facets via query parameter // test facet with json body, insert additional facets via query parameter
client.testJQ(params("stream.body", "{query:'*:*', facet:{x:'unique(where_s)'}}", "stream.contentType", "application/json", "json.facet.y","{terms:{field:where_s}}", "json.facet.z","'unique(where_s)'") client.testJQ(params(CommonParams.STREAM_BODY, "{query:'*:*', facet:{x:'unique(where_s)'}}", "stream.contentType", "application/json", "json.facet.y","{terms:{field:where_s}}", "json.facet.z","'unique(where_s)'")
, "facets=={count:6,x:2, y:{buckets:[{val:NJ,count:3},{val:NY,count:2}]}, z:2}" , "facets=={count:6,x:2, y:{buckets:[{val:NJ,count:3},{val:NY,count:2}]}, z:2}"
); );

View File

@ -117,6 +117,7 @@ The properties that are configured with these commands are predefined and listed
* `requestDispatcher.requestParsers.multipartUploadLimitInKB` * `requestDispatcher.requestParsers.multipartUploadLimitInKB`
* `requestDispatcher.requestParsers.formdataUploadLimitInKB` * `requestDispatcher.requestParsers.formdataUploadLimitInKB`
* `requestDispatcher.requestParsers.enableRemoteStreaming` * `requestDispatcher.requestParsers.enableRemoteStreaming`
* `requestDispatcher.requestParsers.enableStreamBody`
* `requestDispatcher.requestParsers.addHttpRequestToContext` * `requestDispatcher.requestParsers.addHttpRequestToContext`
[[ConfigAPI-CommandsforCustomHandlersandLocalComponents]] [[ConfigAPI-CommandsforCustomHandlersandLocalComponents]]

View File

@ -102,7 +102,7 @@ Example of `commit` and `optimize` with optional attributes:
[[NearRealTimeSearching-PassingcommitandcommitWithinparametersaspartoftheURL]] [[NearRealTimeSearching-PassingcommitandcommitWithinparametersaspartoftheURL]]
=== Passing commit and commitWithin Parameters as Part of the URL === Passing commit and commitWithin Parameters as Part of the URL
Update handlers can also get `commit`-related parameters as part of the update URL. This example adds a small test document and causes an explicit commit to happen immediately afterwards: Update handlers can also get `commit`-related parameters as part of the update URL, if the `stream.body` feature is enabled. This example adds a small test document and causes an explicit commit to happen immediately afterwards:
[source,text] [source,text]
---- ----
@ -132,6 +132,8 @@ curl http://localhost:8983/solr/my_collection/update?commitWithin=10000
-H "Content-Type: text/xml" --data-binary '<add><doc><field name="id">testdoc</field></doc></add>' -H "Content-Type: text/xml" --data-binary '<add><doc><field name="id">testdoc</field></doc></add>'
---- ----
WARNING: While the `stream.body` feature is great for development and testing, it should normally not be enabled in production systems, as it lets a user with READ permissions post data that may alter the system state. The feature is disabled by default. See <<requestdispatcher-in-solrconfig.adoc#RequestDispatcherinSolrConfig-requestParsersElement,RequestDispatcher in SolrConfig>> for details.
[[NearRealTimeSearching-ChangingdefaultcommitWithinBehavior]] [[NearRealTimeSearching-ChangingdefaultcommitWithinBehavior]]
=== Changing default commitWithin Behavior === Changing default commitWithin Behavior

View File

@ -48,6 +48,8 @@ The `<requestParsers>` sub-element controls values related to parsing requests.
The attribute `enableRemoteStreaming` controls whether remote streaming of content is allowed. If omitted or set to `false` (the default), streaming will not be allowed. Setting it to `true` lets you specify the location of content to be streamed using `stream.file` or `stream.url` parameters. The attribute `enableRemoteStreaming` controls whether remote streaming of content is allowed. If omitted or set to `false` (the default), streaming will not be allowed. Setting it to `true` lets you specify the location of content to be streamed using `stream.file` or `stream.url` parameters.
The attribute `enableStreamBody` controls whether streaming content from the HTTP parameter `stream.body` is allowed. If omitted or set to `false` (the default), streaming will not be allowed. Setting it to `true` lets you pass data in the `stream.body` parameter.
If you enable remote streaming, be sure that you have authentication enabled. Otherwise, someone could potentially gain access to your content by accessing arbitrary URLs. It's also a good idea to place Solr behind a firewall to prevent it from being accessed from untrusted clients. If you enable remote streaming, be sure that you have authentication enabled. Otherwise, someone could potentially gain access to your content by accessing arbitrary URLs. It's also a good idea to place Solr behind a firewall to prevent it from being accessed from untrusted clients.
The attribute `multipartUploadLimitInKB` sets an upper limit in kilobytes on the size of a document that may be submitted in a multi-part HTTP POST request. The value specified is multiplied by 1024 to determine the size in bytes. A value of `-1` means MAX_INT, which is also the system default if omitted. The attribute `multipartUploadLimitInKB` sets an upper limit in kilobytes on the size of a document that may be submitted in a multi-part HTTP POST request. The value specified is multiplied by 1024 to determine the size in bytes. A value of `-1` means MAX_INT, which is also the system default if omitted.
@ -59,11 +61,22 @@ The attribute `addHttpRequestToContext` can be used to indicate that the origina
[source,xml] [source,xml]
---- ----
<requestParsers enableRemoteStreaming="false" <requestParsers enableRemoteStreaming="false"
enableStreamBody="false"
multipartUploadLimitInKB="2048" multipartUploadLimitInKB="2048"
formdataUploadLimitInKB="2048" formdataUploadLimitInKB="2048"
addHttpRequestToContext="false" /> addHttpRequestToContext="false" />
---- ----
The below command is an example of how to enable RemoteStreaming and BodyStreaming through <<config-api.adoc#ConfigAPI-CreatingandUpdatingCommonProperties,Config API>>:
[source,bash]
----
curl http://localhost:8983/solr/gettingstarted/config -H 'Content-type:application/json' -d'{
"set-property" : {"requestDispatcher.requestParsers.enableRemoteStreaming":true},
"set-property" : {"requestDispatcher.requestParsers.enableStreamBody":true}
}'
----
[[RequestDispatcherinSolrConfig-httpCachingElement]] [[RequestDispatcherinSolrConfig-httpCachingElement]]
== httpCaching Element == httpCaching Element

View File

@ -168,7 +168,7 @@ For posting XML messages contained in a file, you can use the alternative form:
curl http://localhost:8983/solr/my_collection/update -H "Content-Type: text/xml" --data-binary @myfile.xml curl http://localhost:8983/solr/my_collection/update -H "Content-Type: text/xml" --data-binary @myfile.xml
---- ----
Short requests can also be sent using a HTTP GET command, URL-encoding the request, as in the following. Note the escaping of "<" and ">": Short requests can also be sent using a HTTP GET command, if enabled in <<requestdispatcher-in-solrconfig.adoc#RequestDispatcherinSolrConfig-requestParsersElement,RequestDispatcher in SolrConfig>> element, URL-encoding the request, as in the following. Note the escaping of "<" and ">":
[source,bash] [source,bash]
---- ----