REST API: Failure to index docs that have their ids URL encoded and contain `/`, closes #681.

This commit is contained in:
kimchy 2011-02-10 03:18:01 +02:00
parent 89ac4d108a
commit 57108c8575
9 changed files with 78 additions and 37 deletions

View File

@ -30,20 +30,37 @@ import static org.elasticsearch.common.collect.MapBuilder.*;
* @author kimchy (Shay Banon)
*/
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 Pattern pattern;
private T rootValue;
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);
root = new TrieNode<T>(separator, null, null, wildcard);
}
public static class TrieNode<T> {
public class TrieNode<T> {
private transient String key;
private transient T value;
private boolean isWildcard;
@ -169,6 +186,10 @@ public class PathTrie<T> {
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) {
@ -204,8 +225,4 @@ public class PathTrie<T> {
}
return root.retrieve(strings, index, params);
}
private static void put(Map<String, String> params, String key, String value) {
params.put(key, value);
}
}

View File

@ -29,6 +29,7 @@ import org.elasticsearch.rest.RestController;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.rest.StringRestResponse;
import org.elasticsearch.rest.XContentThrowableRestResponse;
import org.elasticsearch.rest.support.RestUtils;
import org.elasticsearch.threadpool.ThreadPool;
import java.io.IOException;
@ -48,12 +49,12 @@ public class HttpServer extends AbstractLifecycleComponent<HttpServer> {
private final TransportNodesInfoAction nodesInfoAction;
private final PathTrie<HttpServerHandler> getHandlers = new PathTrie<HttpServerHandler>();
private final PathTrie<HttpServerHandler> postHandlers = new PathTrie<HttpServerHandler>();
private final PathTrie<HttpServerHandler> putHandlers = new PathTrie<HttpServerHandler>();
private final PathTrie<HttpServerHandler> deleteHandlers = new PathTrie<HttpServerHandler>();
private final PathTrie<HttpServerHandler> headHandlers = new PathTrie<HttpServerHandler>();
private final PathTrie<HttpServerHandler> optionsHandlers = new PathTrie<HttpServerHandler>();
private final PathTrie<HttpServerHandler> getHandlers = new PathTrie<HttpServerHandler>(RestUtils.REST_DECODER);
private final PathTrie<HttpServerHandler> postHandlers = new PathTrie<HttpServerHandler>(RestUtils.REST_DECODER);
private final PathTrie<HttpServerHandler> putHandlers = new PathTrie<HttpServerHandler>(RestUtils.REST_DECODER);
private final PathTrie<HttpServerHandler> deleteHandlers = new PathTrie<HttpServerHandler>(RestUtils.REST_DECODER);
private final PathTrie<HttpServerHandler> headHandlers = new PathTrie<HttpServerHandler>(RestUtils.REST_DECODER);
private final PathTrie<HttpServerHandler> optionsHandlers = new PathTrie<HttpServerHandler>(RestUtils.REST_DECODER);
@Inject public HttpServer(Settings settings, HttpServerTransport transport, ThreadPool threadPool,
RestController restController, TransportNodesInfoAction nodesInfoAction) {
@ -166,6 +167,9 @@ public class HttpServer extends AbstractLifecycleComponent<HttpServer> {
}
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();
}
}

View File

@ -39,7 +39,7 @@ public class NettyHttpRequest extends AbstractRestRequest implements HttpRequest
private final Map<String, String> params;
private final String path;
private final String rawPath;
private byte[] cachedData;
@ -50,9 +50,9 @@ public class NettyHttpRequest extends AbstractRestRequest implements HttpRequest
String uri = request.getUri();
int pathEndPos = uri.indexOf('?');
if (pathEndPos < 0) {
this.path = RestUtils.decodeComponent(uri);
this.rawPath = uri;
} else {
this.path = RestUtils.decodeComponent(uri.substring(0, pathEndPos));
this.rawPath = uri.substring(0, pathEndPos);
RestUtils.decodeQueryString(uri, pathEndPos + 1, params);
}
}
@ -86,8 +86,8 @@ public class NettyHttpRequest extends AbstractRestRequest implements HttpRequest
return request.getUri();
}
@Override public String path() {
return path;
@Override public String rawPath() {
return rawPath;
}
@Override public Map<String, String> params() {

View File

@ -25,6 +25,7 @@ import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.path.PathTrie;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.rest.support.RestUtils;
import java.io.IOException;
@ -33,12 +34,12 @@ import java.io.IOException;
*/
public class RestController extends AbstractLifecycleComponent<RestController> {
private final PathTrie<RestHandler> getHandlers = new PathTrie<RestHandler>();
private final PathTrie<RestHandler> postHandlers = new PathTrie<RestHandler>();
private final PathTrie<RestHandler> putHandlers = new PathTrie<RestHandler>();
private final PathTrie<RestHandler> deleteHandlers = new PathTrie<RestHandler>();
private final PathTrie<RestHandler> headHandlers = new PathTrie<RestHandler>();
private final PathTrie<RestHandler> optionsHandlers = new PathTrie<RestHandler>();
private final PathTrie<RestHandler> getHandlers = new PathTrie<RestHandler>(RestUtils.REST_DECODER);
private final PathTrie<RestHandler> postHandlers = new PathTrie<RestHandler>(RestUtils.REST_DECODER);
private final PathTrie<RestHandler> putHandlers = new PathTrie<RestHandler>(RestUtils.REST_DECODER);
private final PathTrie<RestHandler> deleteHandlers = new PathTrie<RestHandler>(RestUtils.REST_DECODER);
private final PathTrie<RestHandler> headHandlers = new PathTrie<RestHandler>(RestUtils.REST_DECODER);
private final PathTrie<RestHandler> optionsHandlers = new PathTrie<RestHandler>(RestUtils.REST_DECODER);
@Inject public RestController(Settings settings) {
super(settings);
@ -116,6 +117,9 @@ public class RestController extends AbstractLifecycleComponent<RestController> {
}
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();
}
}

View File

@ -43,7 +43,12 @@ public interface RestRequest extends ToXContent.Params {
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();

View File

@ -37,6 +37,10 @@ public abstract class AbstractRestRequest implements RestRequest {
private static final Pattern commaPattern = Pattern.compile(",");
@Override public final String path() {
return RestUtils.decodeComponent(rawPath());
}
@Override public float paramAsFloat(String key, float defaultValue) {
String sValue = param(key);
if (sValue == null) {

View File

@ -20,6 +20,7 @@
package org.elasticsearch.rest.support;
import org.elasticsearch.common.base.Charsets;
import org.elasticsearch.common.path.PathTrie;
import java.nio.charset.Charset;
import java.util.Map;
@ -29,6 +30,12 @@ import java.util.Map;
*/
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) {
if (fromIndex < 0) {
return;

View File

@ -45,7 +45,7 @@ public class MemcachedRestRequest extends AbstractRestRequest {
private final Map<String, String> params;
private final String path;
private final String rawPath;
private byte[] data;
@ -62,9 +62,9 @@ public class MemcachedRestRequest extends AbstractRestRequest {
this.params = new HashMap<String, String>();
int pathEndPos = uri.indexOf('?');
if (pathEndPos < 0) {
this.path = uri;
this.rawPath = uri;
} else {
this.path = uri.substring(0, pathEndPos);
this.rawPath = uri.substring(0, pathEndPos);
RestUtils.decodeQueryString(uri, pathEndPos + 1, params);
}
}
@ -77,8 +77,8 @@ public class MemcachedRestRequest extends AbstractRestRequest {
return this.uri;
}
@Override public String path() {
return this.path;
@Override public String rawPath() {
return this.rawPath;
}
public byte[] getUriBytes() {

View File

@ -36,7 +36,7 @@ public class ThriftRestRequest extends AbstractRestRequest implements org.elasti
private final org.elasticsearch.thrift.RestRequest request;
private final String path;
private final String rawPath;
private final Map<String, String> params;
@ -46,9 +46,9 @@ public class ThriftRestRequest extends AbstractRestRequest implements org.elasti
int pathEndPos = request.getUri().indexOf('?');
if (pathEndPos < 0) {
this.path = request.getUri();
this.rawPath = request.getUri();
} else {
this.path = request.getUri().substring(0, pathEndPos);
this.rawPath = request.getUri().substring(0, pathEndPos);
RestUtils.decodeQueryString(request.getUri(), pathEndPos + 1, params);
}
}
@ -75,8 +75,8 @@ public class ThriftRestRequest extends AbstractRestRequest implements org.elasti
return request.getUri();
}
@Override public String path() {
return this.path;
@Override public String rawPath() {
return this.rawPath;
}
@Override public boolean hasContent() {