REST API: Failure to index docs that have their ids URL encoded and contain `/`, closes #681.
This commit is contained in:
parent
89ac4d108a
commit
57108c8575
|
@ -30,20 +30,37 @@ import static org.elasticsearch.common.collect.MapBuilder.*;
|
||||||
* @author kimchy (Shay Banon)
|
* @author kimchy (Shay Banon)
|
||||||
*/
|
*/
|
||||||
public class PathTrie<T> {
|
public class PathTrie<T> {
|
||||||
|
|
||||||
|
public static interface Decoder {
|
||||||
|
String decode(String value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Decoder NO_DECODER = new Decoder() {
|
||||||
|
@Override public String decode(String value) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final Decoder decoder;
|
||||||
private final TrieNode<T> root;
|
private final TrieNode<T> root;
|
||||||
private final Pattern pattern;
|
private final Pattern pattern;
|
||||||
private T rootValue;
|
private T rootValue;
|
||||||
|
|
||||||
public PathTrie() {
|
public PathTrie() {
|
||||||
this("/", "*");
|
this("/", "*", NO_DECODER);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PathTrie(String separator, String wildcard) {
|
public PathTrie(Decoder decoder) {
|
||||||
|
this("/", "*", decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PathTrie(String separator, String wildcard, Decoder decoder) {
|
||||||
|
this.decoder = decoder;
|
||||||
pattern = Pattern.compile(separator);
|
pattern = Pattern.compile(separator);
|
||||||
root = new TrieNode<T>(separator, null, null, wildcard);
|
root = new TrieNode<T>(separator, null, null, wildcard);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TrieNode<T> {
|
public class TrieNode<T> {
|
||||||
private transient String key;
|
private transient String key;
|
||||||
private transient T value;
|
private transient T value;
|
||||||
private boolean isWildcard;
|
private boolean isWildcard;
|
||||||
|
@ -169,6 +186,10 @@ public class PathTrie<T> {
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void put(Map<String, String> params, String key, String value) {
|
||||||
|
params.put(key, decoder.decode(value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void insert(String path, T value) {
|
public void insert(String path, T value) {
|
||||||
|
@ -204,8 +225,4 @@ public class PathTrie<T> {
|
||||||
}
|
}
|
||||||
return root.retrieve(strings, index, params);
|
return root.retrieve(strings, index, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void put(Map<String, String> params, String key, String value) {
|
|
||||||
params.put(key, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.elasticsearch.rest.RestController;
|
||||||
import org.elasticsearch.rest.RestRequest;
|
import org.elasticsearch.rest.RestRequest;
|
||||||
import org.elasticsearch.rest.StringRestResponse;
|
import org.elasticsearch.rest.StringRestResponse;
|
||||||
import org.elasticsearch.rest.XContentThrowableRestResponse;
|
import org.elasticsearch.rest.XContentThrowableRestResponse;
|
||||||
|
import org.elasticsearch.rest.support.RestUtils;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -48,12 +49,12 @@ public class HttpServer extends AbstractLifecycleComponent<HttpServer> {
|
||||||
|
|
||||||
private final TransportNodesInfoAction nodesInfoAction;
|
private final TransportNodesInfoAction nodesInfoAction;
|
||||||
|
|
||||||
private final PathTrie<HttpServerHandler> getHandlers = new PathTrie<HttpServerHandler>();
|
private final PathTrie<HttpServerHandler> getHandlers = new PathTrie<HttpServerHandler>(RestUtils.REST_DECODER);
|
||||||
private final PathTrie<HttpServerHandler> postHandlers = new PathTrie<HttpServerHandler>();
|
private final PathTrie<HttpServerHandler> postHandlers = new PathTrie<HttpServerHandler>(RestUtils.REST_DECODER);
|
||||||
private final PathTrie<HttpServerHandler> putHandlers = new PathTrie<HttpServerHandler>();
|
private final PathTrie<HttpServerHandler> putHandlers = new PathTrie<HttpServerHandler>(RestUtils.REST_DECODER);
|
||||||
private final PathTrie<HttpServerHandler> deleteHandlers = new PathTrie<HttpServerHandler>();
|
private final PathTrie<HttpServerHandler> deleteHandlers = new PathTrie<HttpServerHandler>(RestUtils.REST_DECODER);
|
||||||
private final PathTrie<HttpServerHandler> headHandlers = new PathTrie<HttpServerHandler>();
|
private final PathTrie<HttpServerHandler> headHandlers = new PathTrie<HttpServerHandler>(RestUtils.REST_DECODER);
|
||||||
private final PathTrie<HttpServerHandler> optionsHandlers = new PathTrie<HttpServerHandler>();
|
private final PathTrie<HttpServerHandler> optionsHandlers = new PathTrie<HttpServerHandler>(RestUtils.REST_DECODER);
|
||||||
|
|
||||||
@Inject public HttpServer(Settings settings, HttpServerTransport transport, ThreadPool threadPool,
|
@Inject public HttpServer(Settings settings, HttpServerTransport transport, ThreadPool threadPool,
|
||||||
RestController restController, TransportNodesInfoAction nodesInfoAction) {
|
RestController restController, TransportNodesInfoAction nodesInfoAction) {
|
||||||
|
@ -166,6 +167,9 @@ public class HttpServer extends AbstractLifecycleComponent<HttpServer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPath(HttpRequest request) {
|
private String getPath(HttpRequest request) {
|
||||||
return request.path();
|
// we use rawPath since we don't want to decode it while processing the path resolution
|
||||||
|
// so we can handle things like:
|
||||||
|
// my_index/my_type/http%3A%2F%2Fwww.google.com
|
||||||
|
return request.rawPath();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ public class NettyHttpRequest extends AbstractRestRequest implements HttpRequest
|
||||||
|
|
||||||
private final Map<String, String> params;
|
private final Map<String, String> params;
|
||||||
|
|
||||||
private final String path;
|
private final String rawPath;
|
||||||
|
|
||||||
private byte[] cachedData;
|
private byte[] cachedData;
|
||||||
|
|
||||||
|
@ -50,9 +50,9 @@ public class NettyHttpRequest extends AbstractRestRequest implements HttpRequest
|
||||||
String uri = request.getUri();
|
String uri = request.getUri();
|
||||||
int pathEndPos = uri.indexOf('?');
|
int pathEndPos = uri.indexOf('?');
|
||||||
if (pathEndPos < 0) {
|
if (pathEndPos < 0) {
|
||||||
this.path = RestUtils.decodeComponent(uri);
|
this.rawPath = uri;
|
||||||
} else {
|
} else {
|
||||||
this.path = RestUtils.decodeComponent(uri.substring(0, pathEndPos));
|
this.rawPath = uri.substring(0, pathEndPos);
|
||||||
RestUtils.decodeQueryString(uri, pathEndPos + 1, params);
|
RestUtils.decodeQueryString(uri, pathEndPos + 1, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,8 +86,8 @@ public class NettyHttpRequest extends AbstractRestRequest implements HttpRequest
|
||||||
return request.getUri();
|
return request.getUri();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public String path() {
|
@Override public String rawPath() {
|
||||||
return path;
|
return rawPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public Map<String, String> params() {
|
@Override public Map<String, String> params() {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.elasticsearch.common.component.AbstractLifecycleComponent;
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.path.PathTrie;
|
import org.elasticsearch.common.path.PathTrie;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.rest.support.RestUtils;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@ -33,12 +34,12 @@ import java.io.IOException;
|
||||||
*/
|
*/
|
||||||
public class RestController extends AbstractLifecycleComponent<RestController> {
|
public class RestController extends AbstractLifecycleComponent<RestController> {
|
||||||
|
|
||||||
private final PathTrie<RestHandler> getHandlers = new PathTrie<RestHandler>();
|
private final PathTrie<RestHandler> getHandlers = new PathTrie<RestHandler>(RestUtils.REST_DECODER);
|
||||||
private final PathTrie<RestHandler> postHandlers = new PathTrie<RestHandler>();
|
private final PathTrie<RestHandler> postHandlers = new PathTrie<RestHandler>(RestUtils.REST_DECODER);
|
||||||
private final PathTrie<RestHandler> putHandlers = new PathTrie<RestHandler>();
|
private final PathTrie<RestHandler> putHandlers = new PathTrie<RestHandler>(RestUtils.REST_DECODER);
|
||||||
private final PathTrie<RestHandler> deleteHandlers = new PathTrie<RestHandler>();
|
private final PathTrie<RestHandler> deleteHandlers = new PathTrie<RestHandler>(RestUtils.REST_DECODER);
|
||||||
private final PathTrie<RestHandler> headHandlers = new PathTrie<RestHandler>();
|
private final PathTrie<RestHandler> headHandlers = new PathTrie<RestHandler>(RestUtils.REST_DECODER);
|
||||||
private final PathTrie<RestHandler> optionsHandlers = new PathTrie<RestHandler>();
|
private final PathTrie<RestHandler> optionsHandlers = new PathTrie<RestHandler>(RestUtils.REST_DECODER);
|
||||||
|
|
||||||
@Inject public RestController(Settings settings) {
|
@Inject public RestController(Settings settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
|
@ -116,6 +117,9 @@ public class RestController extends AbstractLifecycleComponent<RestController> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPath(RestRequest request) {
|
private String getPath(RestRequest request) {
|
||||||
return request.path();
|
// we use rawPath since we don't want to decode it while processing the path resolution
|
||||||
|
// so we can handle things like:
|
||||||
|
// my_index/my_type/http%3A%2F%2Fwww.google.com
|
||||||
|
return request.rawPath();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,12 @@ public interface RestRequest extends ToXContent.Params {
|
||||||
String uri();
|
String uri();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The path part of the URI (without the query string).
|
* The non decoded, raw path provided.
|
||||||
|
*/
|
||||||
|
String rawPath();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The path part of the URI (without the query string), decoded.
|
||||||
*/
|
*/
|
||||||
String path();
|
String path();
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,10 @@ public abstract class AbstractRestRequest implements RestRequest {
|
||||||
|
|
||||||
private static final Pattern commaPattern = Pattern.compile(",");
|
private static final Pattern commaPattern = Pattern.compile(",");
|
||||||
|
|
||||||
|
@Override public final String path() {
|
||||||
|
return RestUtils.decodeComponent(rawPath());
|
||||||
|
}
|
||||||
|
|
||||||
@Override public float paramAsFloat(String key, float defaultValue) {
|
@Override public float paramAsFloat(String key, float defaultValue) {
|
||||||
String sValue = param(key);
|
String sValue = param(key);
|
||||||
if (sValue == null) {
|
if (sValue == null) {
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
package org.elasticsearch.rest.support;
|
package org.elasticsearch.rest.support;
|
||||||
|
|
||||||
import org.elasticsearch.common.base.Charsets;
|
import org.elasticsearch.common.base.Charsets;
|
||||||
|
import org.elasticsearch.common.path.PathTrie;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -29,6 +30,12 @@ import java.util.Map;
|
||||||
*/
|
*/
|
||||||
public class RestUtils {
|
public class RestUtils {
|
||||||
|
|
||||||
|
public static PathTrie.Decoder REST_DECODER = new PathTrie.Decoder() {
|
||||||
|
@Override public String decode(String value) {
|
||||||
|
return RestUtils.decodeComponent(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
public static void decodeQueryString(String s, int fromIndex, Map<String, String> params) {
|
public static void decodeQueryString(String s, int fromIndex, Map<String, String> params) {
|
||||||
if (fromIndex < 0) {
|
if (fromIndex < 0) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -45,7 +45,7 @@ public class MemcachedRestRequest extends AbstractRestRequest {
|
||||||
|
|
||||||
private final Map<String, String> params;
|
private final Map<String, String> params;
|
||||||
|
|
||||||
private final String path;
|
private final String rawPath;
|
||||||
|
|
||||||
private byte[] data;
|
private byte[] data;
|
||||||
|
|
||||||
|
@ -62,9 +62,9 @@ public class MemcachedRestRequest extends AbstractRestRequest {
|
||||||
this.params = new HashMap<String, String>();
|
this.params = new HashMap<String, String>();
|
||||||
int pathEndPos = uri.indexOf('?');
|
int pathEndPos = uri.indexOf('?');
|
||||||
if (pathEndPos < 0) {
|
if (pathEndPos < 0) {
|
||||||
this.path = uri;
|
this.rawPath = uri;
|
||||||
} else {
|
} else {
|
||||||
this.path = uri.substring(0, pathEndPos);
|
this.rawPath = uri.substring(0, pathEndPos);
|
||||||
RestUtils.decodeQueryString(uri, pathEndPos + 1, params);
|
RestUtils.decodeQueryString(uri, pathEndPos + 1, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,8 +77,8 @@ public class MemcachedRestRequest extends AbstractRestRequest {
|
||||||
return this.uri;
|
return this.uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public String path() {
|
@Override public String rawPath() {
|
||||||
return this.path;
|
return this.rawPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getUriBytes() {
|
public byte[] getUriBytes() {
|
||||||
|
|
|
@ -36,7 +36,7 @@ public class ThriftRestRequest extends AbstractRestRequest implements org.elasti
|
||||||
|
|
||||||
private final org.elasticsearch.thrift.RestRequest request;
|
private final org.elasticsearch.thrift.RestRequest request;
|
||||||
|
|
||||||
private final String path;
|
private final String rawPath;
|
||||||
|
|
||||||
private final Map<String, String> params;
|
private final Map<String, String> params;
|
||||||
|
|
||||||
|
@ -46,9 +46,9 @@ public class ThriftRestRequest extends AbstractRestRequest implements org.elasti
|
||||||
|
|
||||||
int pathEndPos = request.getUri().indexOf('?');
|
int pathEndPos = request.getUri().indexOf('?');
|
||||||
if (pathEndPos < 0) {
|
if (pathEndPos < 0) {
|
||||||
this.path = request.getUri();
|
this.rawPath = request.getUri();
|
||||||
} else {
|
} else {
|
||||||
this.path = request.getUri().substring(0, pathEndPos);
|
this.rawPath = request.getUri().substring(0, pathEndPos);
|
||||||
RestUtils.decodeQueryString(request.getUri(), pathEndPos + 1, params);
|
RestUtils.decodeQueryString(request.getUri(), pathEndPos + 1, params);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,8 +75,8 @@ public class ThriftRestRequest extends AbstractRestRequest implements org.elasti
|
||||||
return request.getUri();
|
return request.getUri();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public String path() {
|
@Override public String rawPath() {
|
||||||
return this.path;
|
return this.rawPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public boolean hasContent() {
|
@Override public boolean hasContent() {
|
||||||
|
|
Loading…
Reference in New Issue