mirror of https://github.com/apache/lucene.git
SOLR-7217: autodetect POST body for curl
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1667457 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
bf00c1a0a7
commit
695a0f0af0
|
@ -179,6 +179,10 @@ New Features
|
|||
* SOLR-6141: Schema API: Remove fields, dynamic fields, field types and copy
|
||||
fields; and replace fields, dynamic fields and field types. (Steve Rowe)
|
||||
|
||||
* SOLR-7217: HTTP POST body is auto-detected when the client is curl and the content
|
||||
type is form data (curl's default), allowing users to use curl to send
|
||||
JSON or XML without having to specify the content type. (yonik)
|
||||
|
||||
|
||||
Bug Fixes
|
||||
----------------------
|
||||
|
|
|
@ -65,7 +65,7 @@ import org.slf4j.LoggerFactory;
|
|||
|
||||
public class SolrRequestParsers
|
||||
{
|
||||
final Logger log = LoggerFactory.getLogger(SolrRequestParsers.class);
|
||||
final static Logger log = LoggerFactory.getLogger(SolrRequestParsers.class);
|
||||
|
||||
// Should these constants be in a more public place?
|
||||
public static final String MULTIPART = "multipart";
|
||||
|
@ -325,7 +325,7 @@ public class SolrRequestParsers
|
|||
// we already have a charsetDecoder, so we can directly decode without buffering:
|
||||
final String key = decodeChars(keyBytes, keyPos, charsetDecoder),
|
||||
value = decodeChars(valueBytes, valuePos, charsetDecoder);
|
||||
MultiMapSolrParams.addParam(key, value, map);
|
||||
MultiMapSolrParams.addParam(key.trim(), value, map);
|
||||
}
|
||||
} else if (valueStream.size() > 0) {
|
||||
throw new SolrException(ErrorCode.BAD_REQUEST, "application/x-www-form-urlencoded invalid: missing key");
|
||||
|
@ -398,7 +398,7 @@ public class SolrRequestParsers
|
|||
it.remove();
|
||||
final Long valuePos = (Long) it.next();
|
||||
it.remove();
|
||||
MultiMapSolrParams.addParam(decodeChars(keyBytes, keyPos.longValue(), charsetDecoder),
|
||||
MultiMapSolrParams.addParam(decodeChars(keyBytes, keyPos.longValue(), charsetDecoder).trim(),
|
||||
decodeChars(valueBytes, valuePos.longValue(), charsetDecoder), map);
|
||||
}
|
||||
}
|
||||
|
@ -569,7 +569,7 @@ public class SolrRequestParsers
|
|||
// If it's a form field, put it in our parameter map
|
||||
if (item.isFormField()) {
|
||||
MultiMapSolrParams.addParam(
|
||||
item.getFieldName(),
|
||||
item.getFieldName().trim(),
|
||||
item.getString(), params.getMap() );
|
||||
}
|
||||
// Add the stream
|
||||
|
@ -587,44 +587,40 @@ public class SolrRequestParsers
|
|||
*/
|
||||
static class FormDataRequestParser implements SolrRequestParser
|
||||
{
|
||||
private static final long WS_MASK=(1L<<' ')|(1L<<'\t')|(1L<<'\r')|(1L<<'\n')|(1L<<'#')|(1L<<'/')|(0x01); // set 1 bit so 0xA0 will be flagged as possible whitespace
|
||||
|
||||
private final int uploadLimitKB;
|
||||
|
||||
public FormDataRequestParser( int limit )
|
||||
{
|
||||
uploadLimitKB = limit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SolrParams parseParamsAndFillStreams(
|
||||
final HttpServletRequest req, ArrayList<ContentStream> streams ) throws Exception
|
||||
{
|
||||
if (!isFormData(req)) {
|
||||
throw new SolrException( ErrorCode.BAD_REQUEST, "Not application/x-www-form-urlencoded content: "+req.getContentType() );
|
||||
}
|
||||
|
||||
public SolrParams parseParamsAndFillStreams(HttpServletRequest req, ArrayList<ContentStream> streams, InputStream in) throws Exception {
|
||||
final Map<String,String[]> map = new HashMap<>();
|
||||
|
||||
|
||||
// also add possible URL parameters and include into the map (parsed using UTF-8):
|
||||
final String qs = req.getQueryString();
|
||||
if (qs != null) {
|
||||
parseQueryString(qs, map);
|
||||
}
|
||||
|
||||
|
||||
// may be -1, so we check again later. But if it's already greater we can stop processing!
|
||||
final long totalLength = req.getContentLength();
|
||||
final long maxLength = ((long) uploadLimitKB) * 1024L;
|
||||
if (totalLength > maxLength) {
|
||||
throw new SolrException(ErrorCode.BAD_REQUEST, "application/x-www-form-urlencoded content length (" +
|
||||
totalLength + " bytes) exceeds upload limit of " + uploadLimitKB + " KB");
|
||||
totalLength + " bytes) exceeds upload limit of " + uploadLimitKB + " KB");
|
||||
}
|
||||
|
||||
// get query String from request body, using the charset given in content-type:
|
||||
final String cs = ContentStreamBase.getCharsetFromContentType(req.getContentType());
|
||||
final Charset charset = (cs == null) ? StandardCharsets.UTF_8 : Charset.forName(cs);
|
||||
InputStream in = null;
|
||||
|
||||
try {
|
||||
in = req.getInputStream();
|
||||
final long bytesRead = parseFormDataContent(FastInputStream.wrap(in), maxLength, charset, map, false);
|
||||
in = FastInputStream.wrap( in == null ? req.getInputStream() : in);
|
||||
|
||||
final long bytesRead = parseFormDataContent(in, maxLength, charset, map, false);
|
||||
if (bytesRead == 0L && totalLength > 0L) {
|
||||
throw getParameterIncompatibilityException();
|
||||
}
|
||||
|
@ -635,11 +631,21 @@ public class SolrRequestParsers
|
|||
} finally {
|
||||
IOUtils.closeWhileHandlingException(in);
|
||||
}
|
||||
|
||||
|
||||
return new MultiMapSolrParams(map);
|
||||
}
|
||||
|
||||
private SolrException getParameterIncompatibilityException() {
|
||||
|
||||
|
||||
@Override
|
||||
public SolrParams parseParamsAndFillStreams(HttpServletRequest req, ArrayList<ContentStream> streams ) throws Exception {
|
||||
if (!isFormData(req)) {
|
||||
throw new SolrException(ErrorCode.BAD_REQUEST, "Not application/x-www-form-urlencoded content: " + req.getContentType());
|
||||
}
|
||||
|
||||
return parseParamsAndFillStreams(req, streams, null);
|
||||
}
|
||||
|
||||
public static SolrException getParameterIncompatibilityException() {
|
||||
return new SolrException(ErrorCode.SERVER_ERROR,
|
||||
"Solr requires that request parameters sent using application/x-www-form-urlencoded " +
|
||||
"content-type can be read through the request input stream. Unfortunately, the " +
|
||||
|
@ -673,35 +679,168 @@ public class SolrRequestParsers
|
|||
MultipartRequestParser multipart;
|
||||
RawRequestParser raw;
|
||||
FormDataRequestParser formdata;
|
||||
|
||||
|
||||
StandardRequestParser(MultipartRequestParser multi, RawRequestParser raw, FormDataRequestParser formdata)
|
||||
{
|
||||
this.multipart = multi;
|
||||
this.raw = raw;
|
||||
this.formdata = formdata;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public SolrParams parseParamsAndFillStreams(
|
||||
final HttpServletRequest req, ArrayList<ContentStream> streams ) throws Exception
|
||||
{
|
||||
String method = req.getMethod().toUpperCase(Locale.ROOT);
|
||||
if ("GET".equals(method) || "HEAD".equals(method)
|
||||
|| (("PUT".equals(method) || "DELETE".equals(method))
|
||||
&& (req.getRequestURI().contains("/schema")
|
||||
|| req.getRequestURI().contains("/config")))) {
|
||||
return parseQueryString(req.getQueryString());
|
||||
}
|
||||
if ("POST".equals( method ) ) {
|
||||
if (formdata.isFormData(req)) {
|
||||
return formdata.parseParamsAndFillStreams(req, streams);
|
||||
public SolrParams parseParamsAndFillStreams(final HttpServletRequest req, ArrayList<ContentStream> streams ) throws Exception {
|
||||
String contentType = req.getContentType();
|
||||
String method = req.getMethod(); // No need to uppercase... HTTP verbs are case sensitive
|
||||
String uri = req.getRequestURI();
|
||||
boolean isPost = "POST".equals(method);
|
||||
|
||||
// SOLR-6787 changed the behavior of a POST without content type. Previously it would throw an exception,
|
||||
// but now it will use the raw request parser.
|
||||
/***
|
||||
if (contentType == null && isPost) {
|
||||
throw new SolrException(ErrorCode.UNSUPPORTED_MEDIA_TYPE, "Must specify a Content-Type header with POST requests");
|
||||
}
|
||||
***/
|
||||
|
||||
|
||||
// According to previous StandardRequestParser logic (this is a re-written version),
|
||||
// POST was handled normally, but other methods (PUT/DELETE)
|
||||
// were handled by restlet if the URI contained /schema or /config
|
||||
// "handled by restlet" means that we don't attempt to handle any request body here.
|
||||
if (!isPost) {
|
||||
if (contentType == null) {
|
||||
return parseQueryString(req.getQueryString());
|
||||
}
|
||||
if (ServletFileUpload.isMultipartContent(req)) {
|
||||
return multipart.parseParamsAndFillStreams(req, streams);
|
||||
|
||||
// OK, we have a BODY at this point
|
||||
|
||||
boolean restletPath = false;
|
||||
int idx = uri.indexOf("/schema");
|
||||
if (idx >= 0 && uri.endsWith("/schema") || uri.contains("/schema/")) {
|
||||
restletPath = true;
|
||||
}
|
||||
idx = uri.indexOf("/config");
|
||||
if (idx >= 0 && uri.endsWith("/config") || uri.contains("/config/")) {
|
||||
restletPath = true;
|
||||
}
|
||||
|
||||
if (restletPath) {
|
||||
return parseQueryString(req.getQueryString());
|
||||
}
|
||||
|
||||
if ("PUT".equals(method) || "DELETE".equals(method)) {
|
||||
throw new SolrException(ErrorCode.BAD_REQUEST, "Unsupported method: " + method + " for request " + req);
|
||||
}
|
||||
return raw.parseParamsAndFillStreams(req, streams);
|
||||
}
|
||||
throw new SolrException(ErrorCode.BAD_REQUEST, "Unsupported method: " + method + " for request " + req);
|
||||
|
||||
|
||||
if (formdata.isFormData(req)) {
|
||||
String userAgent = req.getHeader("User-Agent");
|
||||
boolean isCurl = userAgent != null && userAgent.startsWith("curl/");
|
||||
FastInputStream input = FastInputStream.wrap( req.getInputStream() );
|
||||
|
||||
if (isCurl) {
|
||||
SolrParams params = autodetect(req, streams, input);
|
||||
if (params != null) return params;
|
||||
}
|
||||
|
||||
return formdata.parseParamsAndFillStreams(req, streams, input);
|
||||
}
|
||||
|
||||
if (ServletFileUpload.isMultipartContent(req)) {
|
||||
return multipart.parseParamsAndFillStreams(req, streams);
|
||||
}
|
||||
|
||||
// some other content-type (json, XML, csv, etc)
|
||||
return raw.parseParamsAndFillStreams(req, streams);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final long WS_MASK=(1L<<' ')|(1L<<'\t')|(1L<<'\r')|(1L<<'\n')|(1L<<'#')|(1L<<'/')|(0x01); // set 1 bit so 0xA0 will be flagged as possible whitespace
|
||||
|
||||
/** Returns the parameter map if a different content type was auto-detected */
|
||||
private static SolrParams autodetect(HttpServletRequest req, ArrayList<ContentStream> streams, FastInputStream in) throws IOException {
|
||||
String detectedContentType = null;
|
||||
boolean shouldClose = true;
|
||||
|
||||
try {
|
||||
in.peek(); // should cause some bytes to be read
|
||||
byte[] arr = in.getBuffer();
|
||||
int pos = in.getPositionInBuffer();
|
||||
int end = in.getEndInBuffer();
|
||||
|
||||
for (int i = pos; i < end - 1; i++) { // we do "end-1" because we check "arr[i+1]" sometimes in the loop body
|
||||
int ch = arr[i];
|
||||
boolean isWhitespace = ((WS_MASK >> ch) & 0x01) != 0 && (ch <= ' ' || ch == 0xa0);
|
||||
if (!isWhitespace) {
|
||||
// first non-whitespace chars
|
||||
if (ch == '#' // single line comment
|
||||
|| (ch == '/' && (arr[i + 1] == '/' || arr[i + 1] == '*')) // single line or multi-line comment
|
||||
|| (ch == '{' || ch == '[') // start of JSON object
|
||||
)
|
||||
{
|
||||
detectedContentType = "application/json";
|
||||
}
|
||||
if (ch == '<') {
|
||||
detectedContentType = "text/xml";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (detectedContentType == null) {
|
||||
shouldClose = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
Long size = null;
|
||||
String v = req.getHeader("Content-Length");
|
||||
if (v != null) {
|
||||
size = Long.valueOf(v);
|
||||
}
|
||||
streams.add(new InputStreamContentStream(in, detectedContentType, size));
|
||||
|
||||
|
||||
final Map<String, String[]> map = new HashMap<>();
|
||||
// also add possible URL parameters and include into the map (parsed using UTF-8):
|
||||
final String qs = req.getQueryString();
|
||||
if (qs != null) {
|
||||
parseQueryString(qs, map);
|
||||
}
|
||||
|
||||
return new MultiMapSolrParams(map);
|
||||
|
||||
} catch (IOException ioe) {
|
||||
throw new SolrException(ErrorCode.BAD_REQUEST, ioe);
|
||||
} catch (IllegalStateException ise) {
|
||||
throw (SolrException) FormDataRequestParser.getParameterIncompatibilityException().initCause(ise);
|
||||
} finally {
|
||||
if (shouldClose) {
|
||||
IOUtils.closeWhileHandlingException(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wrap InputStream as a ContentStream
|
||||
*/
|
||||
static class InputStreamContentStream extends ContentStreamBase {
|
||||
private final InputStream is;
|
||||
|
||||
public InputStreamContentStream(InputStream is, String detectedContentType, Long size ) {
|
||||
this.is = is;
|
||||
this.contentType = detectedContentType;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getStream() throws IOException {
|
||||
return is;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -223,11 +223,9 @@ public class SolrRequestParserTest extends SolrTestCaseJ4 {
|
|||
};
|
||||
|
||||
for( String contentType : ct ) {
|
||||
HttpServletRequest request = createMock(HttpServletRequest.class);
|
||||
HttpServletRequest request = getMock("/solr/select", contentType, postBytes.length);
|
||||
expect(request.getMethod()).andReturn("POST").anyTimes();
|
||||
expect(request.getContentType()).andReturn( contentType ).anyTimes();
|
||||
expect(request.getQueryString()).andReturn(getParams).anyTimes();
|
||||
expect(request.getContentLength()).andReturn(postBytes.length).anyTimes();
|
||||
expect(request.getInputStream()).andReturn(new ByteServletInputStream(postBytes));
|
||||
replay(request);
|
||||
|
||||
|
@ -285,11 +283,9 @@ public class SolrRequestParserTest extends SolrTestCaseJ4 {
|
|||
final String contentType = "application/x-www-form-urlencoded; charset=iso-8859-1";
|
||||
|
||||
// Set up the expected behavior
|
||||
HttpServletRequest request = createMock(HttpServletRequest.class);
|
||||
HttpServletRequest request = getMock("/solr/select", contentType, postBytes.length);
|
||||
expect(request.getMethod()).andReturn("POST").anyTimes();
|
||||
expect(request.getContentType()).andReturn( contentType ).anyTimes();
|
||||
expect(request.getQueryString()).andReturn(getParams).anyTimes();
|
||||
expect(request.getContentLength()).andReturn(postBytes.length).anyTimes();
|
||||
expect(request.getInputStream()).andReturn(new ByteServletInputStream(postBytes));
|
||||
replay(request);
|
||||
|
||||
|
@ -316,11 +312,8 @@ public class SolrRequestParserTest extends SolrTestCaseJ4 {
|
|||
while (large.length() <= limitKBytes * 1024) {
|
||||
large.append('&').append(large);
|
||||
}
|
||||
HttpServletRequest request = createMock(HttpServletRequest.class);
|
||||
HttpServletRequest request = getMock("/solr/select", "application/x-www-form-urlencoded", -1);
|
||||
expect(request.getMethod()).andReturn("POST").anyTimes();
|
||||
expect(request.getContentType()).andReturn("application/x-www-form-urlencoded").anyTimes();
|
||||
// we dont pass a content-length to let the security mechanism limit it:
|
||||
expect(request.getContentLength()).andReturn(-1).anyTimes();
|
||||
expect(request.getQueryString()).andReturn(null).anyTimes();
|
||||
expect(request.getInputStream()).andReturn(new ByteServletInputStream(large.toString().getBytes(StandardCharsets.US_ASCII)));
|
||||
replay(request);
|
||||
|
@ -338,10 +331,7 @@ public class SolrRequestParserTest extends SolrTestCaseJ4 {
|
|||
@Test
|
||||
public void testParameterIncompatibilityException1() throws Exception
|
||||
{
|
||||
HttpServletRequest request = createMock(HttpServletRequest.class);
|
||||
expect(request.getMethod()).andReturn("POST").anyTimes();
|
||||
expect(request.getContentType()).andReturn("application/x-www-form-urlencoded").anyTimes();
|
||||
expect(request.getContentLength()).andReturn(100).anyTimes();
|
||||
HttpServletRequest request = getMock("/solr/select", "application/x-www-form-urlencoded", 100);
|
||||
expect(request.getQueryString()).andReturn(null).anyTimes();
|
||||
// we emulate Jetty that returns empty stream when parameters were parsed before:
|
||||
expect(request.getInputStream()).andReturn(new ServletInputStream() {
|
||||
|
@ -377,10 +367,8 @@ public class SolrRequestParserTest extends SolrTestCaseJ4 {
|
|||
@Test
|
||||
public void testParameterIncompatibilityException2() throws Exception
|
||||
{
|
||||
HttpServletRequest request = createMock(HttpServletRequest.class);
|
||||
HttpServletRequest request = getMock("/solr/select", "application/x-www-form-urlencoded", 100);
|
||||
expect(request.getMethod()).andReturn("POST").anyTimes();
|
||||
expect(request.getContentType()).andReturn("application/x-www-form-urlencoded").anyTimes();
|
||||
expect(request.getContentLength()).andReturn(100).anyTimes();
|
||||
expect(request.getQueryString()).andReturn(null).anyTimes();
|
||||
// we emulate Tomcat that throws IllegalStateException when parameters were parsed before:
|
||||
expect(request.getInputStream()).andThrow(new IllegalStateException());
|
||||
|
@ -398,9 +386,8 @@ public class SolrRequestParserTest extends SolrTestCaseJ4 {
|
|||
|
||||
@Test
|
||||
public void testAddHttpRequestToContext() throws Exception {
|
||||
HttpServletRequest request = createMock(HttpServletRequest.class);
|
||||
HttpServletRequest request = getMock("/solr/select", null, -1);
|
||||
expect(request.getMethod()).andReturn("GET").anyTimes();
|
||||
expect(request.getContentType()).andReturn( "application/x-www-form-urlencoded" ).anyTimes();
|
||||
expect(request.getQueryString()).andReturn("q=title:solr").anyTimes();
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("X-Forwarded-For", "10.0.0.1");
|
||||
|
@ -410,7 +397,6 @@ public class SolrRequestParserTest extends SolrTestCaseJ4 {
|
|||
v.add(entry.getValue());
|
||||
expect(request.getHeaders(entry.getKey())).andReturn(v.elements()).anyTimes();
|
||||
}
|
||||
expect(request.getAttribute(SolrRequestParsers.REQUEST_TIMER_SERVLET_ATTRIBUTE)).andReturn(null).anyTimes();
|
||||
replay(request);
|
||||
|
||||
SolrRequestParsers parsers = new SolrRequestParsers(h.getCore().getSolrConfig());
|
||||
|
@ -426,19 +412,99 @@ public class SolrRequestParserTest extends SolrTestCaseJ4 {
|
|||
}
|
||||
|
||||
public void testPostMissingContentType() throws Exception {
|
||||
HttpServletRequest request = createMock(HttpServletRequest.class);
|
||||
HttpServletRequest request = getMock();
|
||||
expect(request.getMethod()).andReturn("POST").anyTimes();
|
||||
expect(request.getContentType()).andReturn(null).anyTimes();
|
||||
expect(request.getQueryString()).andReturn(null).anyTimes();
|
||||
expect(request.getHeader(anyObject())).andReturn(null).anyTimes();
|
||||
expect(request.getAttribute(SolrRequestParsers.REQUEST_TIMER_SERVLET_ATTRIBUTE)).andReturn(null).anyTimes();
|
||||
replay(request);
|
||||
|
||||
SolrRequestParsers parsers = new SolrRequestParsers(h.getCore().getSolrConfig());
|
||||
try {
|
||||
parsers.parse(h.getCore(), "/select", request);
|
||||
} catch (SolrException e) {
|
||||
log.error("should not throw SolrException", e);
|
||||
fail("should not throw SolrException");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testAutoDetect() throws Exception {
|
||||
String curl = "curl/7.30.0";
|
||||
for (String method : new String[]{"GET","POST"}) {
|
||||
doAutoDetect(null, method, "{}=a", null, "{}", "a"); // unknown agent should not auto-detect
|
||||
doAutoDetect(curl, method, "{}", "application/json", null, null); // curl should auto-detect
|
||||
doAutoDetect(curl, method, " \t\n\r {} ", "application/json", null, null); // starting with whitespace
|
||||
doAutoDetect(curl, method, " \t\n\r // how now brown cow\n {} ", "application/json", null, null); // supporting comments
|
||||
doAutoDetect(curl, method, " \t\n\r #different style comment\n {} ", "application/json", null, null);
|
||||
doAutoDetect(curl, method, " \t\n\r /* C style comment */\n {} ", "application/json", null, null);
|
||||
doAutoDetect(curl, method, " \t\n\r <tag>hi</tag> ", "text/xml", null, null);
|
||||
|
||||
doAutoDetect(curl, method, " \t\r\n aaa=1&bbb=2&ccc=3", null, "bbb", "2"); // params with whitespace first
|
||||
doAutoDetect(curl, method, "/x=foo&aaa=1&bbb=2&ccc=3", null, "/x", "foo"); // param name that looks like a path
|
||||
doAutoDetect(curl, method, " \t\r\n /x=foo&aaa=1&bbb=2&ccc=3", null, "bbb", "2"); // param name that looks like a path
|
||||
}
|
||||
}
|
||||
|
||||
public void doAutoDetect(String userAgent, String method, final String body, String expectedContentType, String expectedKey, String expectedValue) throws Exception {
|
||||
String uri = "/solr/select";
|
||||
String contentType = "application/x-www-form-urlencoded";
|
||||
int contentLength = -1; // does this mean auto-detect?
|
||||
|
||||
HttpServletRequest request = createMock(HttpServletRequest.class);
|
||||
expect(request.getHeader("User-Agent")).andReturn(userAgent).anyTimes();
|
||||
expect(request.getHeader("Content-Length")).andReturn(null).anyTimes();
|
||||
expect(request.getRequestURI()).andReturn(uri).anyTimes();
|
||||
expect(request.getContentType()).andReturn(contentType).anyTimes();
|
||||
expect(request.getContentLength()).andReturn(contentLength).anyTimes();
|
||||
expect(request.getAttribute(SolrRequestParsers.REQUEST_TIMER_SERVLET_ATTRIBUTE)).andReturn(null).anyTimes();
|
||||
|
||||
expect(request.getMethod()).andReturn(method).anyTimes();
|
||||
// we dont pass a content-length to let the security mechanism limit it:
|
||||
expect(request.getQueryString()).andReturn("foo=1&bar=2").anyTimes();
|
||||
expect(request.getInputStream()).andReturn(new ByteServletInputStream(body.getBytes(StandardCharsets.US_ASCII)));
|
||||
replay(request);
|
||||
|
||||
SolrRequestParsers parsers = new SolrRequestParsers(h.getCore().getSolrConfig());
|
||||
SolrQueryRequest req = parsers.parse(h.getCore(), "/select", request);
|
||||
int num=0;
|
||||
if (expectedContentType != null) {
|
||||
for (ContentStream cs : req.getContentStreams()) {
|
||||
num++;
|
||||
assertTrue(cs.getContentType().startsWith(expectedContentType));
|
||||
String returnedBody = IOUtils.toString(cs.getReader());
|
||||
assertEquals(body, returnedBody);
|
||||
}
|
||||
assertEquals(1, num);
|
||||
}
|
||||
|
||||
assertEquals("1", req.getParams().get("foo"));
|
||||
assertEquals("2", req.getParams().get("bar"));
|
||||
|
||||
if (expectedKey != null) {
|
||||
assertEquals(expectedValue, req.getParams().get(expectedKey));
|
||||
}
|
||||
|
||||
req.close();
|
||||
}
|
||||
|
||||
|
||||
public HttpServletRequest getMock() {
|
||||
return getMock("/solr/select", null, -1);
|
||||
// return getMock("/solr/select", "application/x-www-form-urlencoded");
|
||||
}
|
||||
|
||||
public HttpServletRequest getMock(String uri, String contentType, int contentLength) {
|
||||
HttpServletRequest request = createMock(HttpServletRequest.class);
|
||||
expect(request.getHeader("User-Agent")).andReturn(null).anyTimes();
|
||||
expect(request.getRequestURI()).andReturn(uri).anyTimes();
|
||||
expect(request.getContentType()).andReturn(contentType).anyTimes();
|
||||
expect(request.getContentLength()).andReturn(contentLength).anyTimes();
|
||||
expect(request.getAttribute(SolrRequestParsers.REQUEST_TIMER_SERVLET_ATTRIBUTE)).andReturn(null).anyTimes();
|
||||
return request;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -144,7 +144,7 @@ public class AutoCommitTest extends AbstractSolrTestCase {
|
|||
{
|
||||
ArrayList<ContentStream> streams = new ArrayList<>();
|
||||
ContentStreamBase stream = new ContentStreamBase.StringStream( str );
|
||||
stream.setContentType( contentType );
|
||||
if (contentType != null) stream.setContentType( contentType );
|
||||
streams.add( stream );
|
||||
return streams;
|
||||
}
|
||||
|
|
|
@ -107,6 +107,7 @@ public abstract class ContentStreamBase implements ContentStream
|
|||
@Override
|
||||
public String getContentType() {
|
||||
if(contentType==null) {
|
||||
// TODO: this is buggy... does not allow for whitespace, JSON comments, etc.
|
||||
InputStream stream = null;
|
||||
try {
|
||||
stream = new FileInputStream(file);
|
||||
|
@ -140,29 +141,40 @@ public abstract class ContentStreamBase implements ContentStream
|
|||
public static class StringStream extends ContentStreamBase
|
||||
{
|
||||
private final String str;
|
||||
|
||||
|
||||
public StringStream( String str ) {
|
||||
this.str = str;
|
||||
|
||||
contentType = null;
|
||||
this(str, detect(str));
|
||||
}
|
||||
|
||||
public StringStream( String str, String contentType ) {
|
||||
this.str = str;
|
||||
this.contentType = contentType;
|
||||
name = null;
|
||||
size = new Long( str.length() );
|
||||
sourceInfo = "string";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentType() {
|
||||
if(contentType==null && str.length() > 0) {
|
||||
char first = str.charAt(0);
|
||||
if(first == '<') {
|
||||
return "application/xml";
|
||||
public static String detect(String str) {
|
||||
String detectedContentType = null;
|
||||
int lim = str.length() - 1;
|
||||
for (int i=0; i<lim; i++) {
|
||||
char ch = str.charAt(i);
|
||||
if (Character.isWhitespace(ch)) {
|
||||
continue;
|
||||
}
|
||||
if(first == '{') {
|
||||
return "application/json";
|
||||
// first non-whitespace chars
|
||||
if (ch == '#' // single line comment
|
||||
|| (ch == '/' && (str.charAt(i + 1) == '/' || str.charAt(i + 1) == '*')) // single line or multi-line comment
|
||||
|| (ch == '{' || ch == '[') // start of JSON object
|
||||
)
|
||||
{
|
||||
detectedContentType = "application/json";
|
||||
} else if (ch == '<') {
|
||||
detectedContentType = "text/xml";
|
||||
}
|
||||
// find a comma? for CSV?
|
||||
break;
|
||||
}
|
||||
return contentType;
|
||||
return detectedContentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -96,6 +96,21 @@ public class FastInputStream extends DataInputInputStream {
|
|||
return end - pos;
|
||||
}
|
||||
|
||||
/** Returns the internal buffer used for caching */
|
||||
public byte[] getBuffer() {
|
||||
return buf;
|
||||
}
|
||||
|
||||
/** Current position within the internal buffer */
|
||||
public int getPositionInBuffer() {
|
||||
return pos;
|
||||
}
|
||||
|
||||
/** Current end-of-data position within the internal buffer. This is one past the last valid byte. */
|
||||
public int getEndInBuffer() {
|
||||
return end;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte b[], int off, int len) throws IOException {
|
||||
int r=0; // number of bytes we have read
|
||||
|
|
Loading…
Reference in New Issue