Permalink
Browse files

handle databases with embedded slashes

  • Loading branch information...
1 parent 0e03eb2 commit c11d33ee3cd63ae522bbaf53ebcd3bcf72b8ceed Robert Newson committed Dec 21, 2009
View
5 couchdb-external-hook.py
@@ -67,9 +67,12 @@ def respond(res, req, host, port):
# Drop name of external hook.
del path[1]
+ # URL-escape each part
+ for index, item in enumerate(path):
+ path[index] = urllib.quote(path[index], "")
if req["query"] == {}:
- path = '/'.join(['', 'info', host, str(port)] + path)
+ path = '/'.join(['', 'info', host, str(port)], path)
else:
path = '/'.join(['', 'search', host, str(port)] + path)
path = '?'.join([path, urllib.urlencode(req["query"])])
View
13 src/main/java/com/github/rnewson/couchdb/lucene/AdminServlet.java
@@ -10,6 +10,8 @@
import org.apache.lucene.index.IndexWriter;
import com.github.rnewson.couchdb.lucene.Lucene.WriterCallback;
+import com.github.rnewson.couchdb.lucene.util.IndexPath;
+import com.github.rnewson.couchdb.lucene.util.ServletUtils;
import com.github.rnewson.couchdb.lucene.util.Utils;
/**
@@ -39,8 +41,15 @@ public void setLucene(final Lucene lucene) {
protected void doPost(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
final String command = req.getParameter("cmd");
+ final IndexPath path = IndexPath.parse(req);
+
+ if (path == null) {
+ ServletUtils.sendJSONError(req, resp, 400, "Bad path");
+ return;
+ }
+
if ("expunge".equals(command)) {
- lucene.withWriter(Utils.getPath(req), new WriterCallback() {
+ lucene.withWriter(path, new WriterCallback() {
public boolean callback(final IndexWriter writer) throws IOException {
writer.expungeDeletes(false);
return false;
@@ -55,7 +64,7 @@ public void onMissing() throws IOException {
}
if ("optimize".equals(command)) {
- lucene.withWriter(Utils.getPath(req), new WriterCallback() {
+ lucene.withWriter(path, new WriterCallback() {
public boolean callback(final IndexWriter writer) throws IOException {
writer.optimize(false);
return false;
View
11 src/main/java/com/github/rnewson/couchdb/lucene/InfoServlet.java
@@ -15,6 +15,8 @@
import org.apache.lucene.index.IndexReader.FieldOption;
import com.github.rnewson.couchdb.lucene.Lucene.ReaderCallback;
+import com.github.rnewson.couchdb.lucene.util.IndexPath;
+import com.github.rnewson.couchdb.lucene.util.ServletUtils;
import com.github.rnewson.couchdb.lucene.util.Utils;
/**
@@ -35,7 +37,14 @@ public void setLucene(final Lucene lucene) {
@Override
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
- lucene.withReader(Utils.getPath(req), Utils.getStaleOk(req), new ReaderCallback() {
+ final IndexPath path = IndexPath.parse(req);
+
+ if (path == null) {
+ ServletUtils.sendJSONError(req, resp, 400, "Bad path");
+ return;
+ }
+
+ lucene.withReader(path, Utils.getStaleOk(req), new ReaderCallback() {
public void callback(final IndexReader reader) throws IOException {
final JSONObject result = new JSONObject();
result.put("current", reader.isCurrent());
View
15 src/main/java/com/github/rnewson/couchdb/lucene/Lucene.java
@@ -18,12 +18,13 @@
import org.apache.lucene.store.FSDirectory;
import com.github.rnewson.couchdb.lucene.util.Constants;
+import com.github.rnewson.couchdb.lucene.util.IndexPath;
public final class Lucene {
private final File root;
- private final IdempotentExecutor<String, ViewIndexer> executor = new IdempotentExecutor<String, ViewIndexer>();
- private final Map<String, Tuple> map = new HashMap<String, Tuple>();
+ private final IdempotentExecutor<IndexPath, ViewIndexer> executor = new IdempotentExecutor<IndexPath, ViewIndexer>();
+ private final Map<IndexPath, Tuple> map = new HashMap<IndexPath, Tuple>();
private static class Tuple {
private String version;
@@ -69,12 +70,12 @@ public Lucene(final File root) {
this.root = root;
}
- public void startIndexing(final String path, final boolean staleOk) {
+ public void startIndexing(final IndexPath path, final boolean staleOk) {
final ViewIndexer viewIndexer = executor.submit(path, new ViewIndexer(this, path, staleOk));
viewIndexer.awaitInitialIndexing();
}
- public void withReader(final String path, final boolean staleOk, final ReaderCallback callback) throws IOException {
+ public void withReader(final IndexPath path, final boolean staleOk, final ReaderCallback callback) throws IOException {
final Tuple tuple;
synchronized (map) {
tuple = map.get(path);
@@ -113,7 +114,7 @@ public void withReader(final String path, final boolean staleOk, final ReaderCal
}
}
- public void withSearcher(final String path, final boolean staleOk, final SearcherCallback callback) throws IOException {
+ public void withSearcher(final IndexPath path, final boolean staleOk, final SearcherCallback callback) throws IOException {
withReader(path, staleOk, new ReaderCallback() {
public void callback(final IndexReader reader) throws IOException {
@@ -126,7 +127,7 @@ public void onMissing() throws IOException {
});
}
- public void withWriter(final String path, final WriterCallback callback) throws IOException {
+ public void withWriter(final IndexPath path, final WriterCallback callback) throws IOException {
final Tuple tuple;
synchronized (map) {
tuple = map.get(path);
@@ -148,7 +149,7 @@ public void withWriter(final String path, final WriterCallback callback) throws
}
}
- public void createWriter(final String path, final UUID uuid, final String function) throws IOException {
+ public void createWriter(final IndexPath path, final UUID uuid, final String function) throws IOException {
final String digest = digest(function);
final File dir = new File(new File(root, uuid.toString()), digest);
dir.mkdirs();
View
18 src/main/java/com/github/rnewson/couchdb/lucene/SearchServlet.java
@@ -39,6 +39,7 @@
import com.github.rnewson.couchdb.lucene.couchdb.Database;
import com.github.rnewson.couchdb.lucene.util.Analyzers;
import com.github.rnewson.couchdb.lucene.util.Constants;
+import com.github.rnewson.couchdb.lucene.util.IndexPath;
import com.github.rnewson.couchdb.lucene.util.ServletUtils;
import com.github.rnewson.couchdb.lucene.util.StopWatch;
import com.github.rnewson.couchdb.lucene.util.Utils;
@@ -71,12 +72,14 @@ protected void doGet(final HttpServletRequest req, final HttpServletResponse res
final boolean rewrite_query = getBooleanParameter(req, "rewrite_query");
final boolean staleOk = Utils.getStaleOk(req);
- if (!Utils.validatePath(Utils.getPath(req))) {
+ final IndexPath path = IndexPath.parse(req);
+
+ if (path == null) {
ServletUtils.sendJSONError(req, resp, 400, "Bad path");
return;
}
- lucene.startIndexing(Utils.getPath(req), staleOk);
+ lucene.startIndexing(path, staleOk);
final SearcherCallback callback = new SearcherCallback() {
@@ -196,13 +199,12 @@ public void callback(final IndexSearcher searcher, final String version) throws
}
// Fetch documents (if requested).
if (include_docs && fetch_ids.length > 0) {
- final String url = String.format("http://%s:%d/", Utils.getHost(Utils.getPath(req)), Utils.getPort(Utils
- .getPath(req)));
+ final String url = String.format("http://%s:%d/", path.getHost(), path.getPort());
+
final HttpClient httpClient = HttpClientFactory.getInstance();
try {
final Couch couch = Couch.getInstance(httpClient, url);
- final Database database = couch.getDatabase(Utils.getDatabase(Utils.getPath(req)));
-
+ final Database database = couch.getDatabase(path.getDatabase());
final JSONArray fetched_docs = database.getDocuments(fetch_ids).getJSONArray("rows");
for (int i = 0; i < max; i++) {
rows.getJSONObject(i).put("doc", fetched_docs.getJSONObject(i).getJSONObject("doc"));
@@ -249,10 +251,10 @@ public void callback(final IndexSearcher searcher, final String version) throws
}
public void onMissing() throws IOException {
- ServletUtils.sendJSONError(req, resp, 404, "Index for " + Utils.getPath(req) + " is missing.");
+ ServletUtils.sendJSONError(req, resp, 404, "Index for " + path + " is missing.");
}
};
- lucene.withSearcher(req.getPathInfo(), staleOk, callback);
+ lucene.withSearcher(path, staleOk, callback);
}
}
View
18 src/main/java/com/github/rnewson/couchdb/lucene/ViewIndexer.java
@@ -36,7 +36,7 @@
import com.github.rnewson.couchdb.lucene.couchdb.Database;
import com.github.rnewson.couchdb.lucene.util.Analyzers;
import com.github.rnewson.couchdb.lucene.util.Constants;
-import com.github.rnewson.couchdb.lucene.util.Utils;
+import com.github.rnewson.couchdb.lucene.util.IndexPath;
public final class ViewIndexer implements Runnable {
@@ -115,7 +115,7 @@ public Void handleResponse(final HttpResponse response) throws IOException {
}
final String id = doc.getString("_id");
- if (id.equals("_design/" + Utils.getDesignDocumentName(path))) {
+ if (id.equals("_design/" + path.getDesignDocumentName())) {
if (doc.optBoolean("_deleted")) {
logger.info("Design document for this view was deleted.");
break loop;
@@ -251,11 +251,11 @@ private void setPendingCommit(final boolean pendingCommit) {
private final Logger logger;
private final Lucene lucene;
- private final String path;
+ private final IndexPath path;
private final boolean staleOk;
- public ViewIndexer(final Lucene lucene, final String path, final boolean staleOk) {
+ public ViewIndexer(final Lucene lucene, final IndexPath path, final boolean staleOk) {
this.lucene = lucene;
this.logger = Logger.getLogger(ViewIndexer.class.getName() + "." + path);
this.path = path;
@@ -307,10 +307,10 @@ private JSONObject extractView(final JSONObject ddoc) {
return null;
}
final JSONObject fulltext = ddoc.getJSONObject("fulltext");
- if (!fulltext.has(Utils.getViewName(path))) {
+ if (!fulltext.has(path.getViewName())) {
return null;
}
- return fulltext.getJSONObject(Utils.getViewName(path));
+ return fulltext.getJSONObject(path.getViewName());
}
private UUID getDatabaseUuid() throws IOException {
@@ -337,7 +337,7 @@ private UUID getDatabaseUuid() throws IOException {
private void index() throws IOException {
final UUID uuid = getDatabaseUuid();
- final JSONObject ddoc = database.getDocument("_design/" + Utils.getDesignDocumentName(path));
+ final JSONObject ddoc = database.getDocument("_design/" + path.getDesignDocumentName());
final JSONObject view = extractView(ddoc);
if (view == null) {
return;
@@ -356,9 +356,9 @@ private void setup() throws IOException {
context.setClassShutter(new RestrictiveClassShutter());
context.setOptimizationLevel(9);
client = HttpClientFactory.getInstance();
- final String url = String.format("http://%s:%d/", Utils.getHost(path), Utils.getPort(path));
+ final String url = String.format("http://%s:%d/", path.getHost(), path.getPort());
final Couch couch = Couch.getInstance(client, url);
- database = couch.getDatabase(Utils.getDatabase(path));
+ database = couch.getDatabase(path.getDatabase());
}
private void teardown() {
View
104 src/main/java/com/github/rnewson/couchdb/lucene/util/IndexPath.java
@@ -0,0 +1,104 @@
+package com.github.rnewson.couchdb.lucene.util;
+
+import javax.servlet.http.HttpServletRequest;
+
+public final class IndexPath {
+
+ public static IndexPath parse(final HttpServletRequest req) {
+ final String uri = req.getRequestURI().replaceFirst("^/\\w+/", "");
+ final String[] parts = uri.split("/");
+ if (parts.length != 5) {
+ return null;
+ }
+ try {
+ return new IndexPath(parts[0], Integer.parseInt(parts[1]), parts[2], parts[3], parts[4]);
+ } catch (final NumberFormatException e) {
+ return null;
+ }
+ }
+
+ private final String database;
+ private final String designDocumentName;
+ private final String host;
+ private final int port;
+
+ private final String viewName;
+
+ public IndexPath(final String host, final int port, final String database, final String designDocumentName,
+ final String viewName) {
+ this.host = host;
+ this.port = port;
+ this.database = database;
+ this.designDocumentName = designDocumentName;
+ this.viewName = viewName;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof IndexPath)) {
+ return false;
+ }
+ final IndexPath other = (IndexPath) obj;
+ if (!database.equals(other.database)) {
+ return false;
+ }
+ if (!designDocumentName.equals(other.designDocumentName)) {
+ return false;
+ }
+ if (!host.equals(other.host)) {
+ return false;
+ }
+ if (port != other.port) {
+ return false;
+ }
+ if (!viewName.equals(other.viewName)) {
+ return false;
+ }
+ return true;
+ }
+
+ public String getDatabase() {
+ return database;
+ }
+
+ public String getDesignDocumentName() {
+ return designDocumentName;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public String getViewName() {
+ return viewName;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + database.hashCode();
+ result = prime * result + designDocumentName.hashCode();
+ result = prime * result + host.hashCode();
+ result = prime * result + port;
+ result = prime * result + viewName.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "IndexPath [host=" + host + ", port=" + port + ", database=" + database + ", designDocumentName="
+ + designDocumentName + ", viewName=" + viewName + "]";
+ }
+
+}
View
37 src/main/java/com/github/rnewson/couchdb/lucene/util/Utils.java
@@ -21,7 +21,6 @@
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
-import java.util.Arrays;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -67,42 +66,6 @@ public static String urlEncode(final String path) {
}
}
- public static String getHost(final String path) {
- return split(path, true)[0];
- }
-
- public static int getPort(final String path) {
- return Integer.parseInt(split(path, true)[1]);
- }
-
- public static String getDatabase(final String path) {
- return split(path, true)[2];
- }
-
- public static String getDesignDocumentName(final String path) {
- return split(path, true)[3];
- }
-
- public static String getViewName(final String path) {
- return split(path, true)[4];
- }
-
- public static boolean validatePath(final String path) {
- return split(path, false).length == 5;
- }
-
- private static String[] split(final String path, final boolean throwIfWrong) {
- final String[] result = path.substring(1).split("/");
- if (throwIfWrong && result.length != 5) {
- throw new IllegalArgumentException("Malformed path (" + Arrays.toString(result) + ")");
- }
- return result;
- }
-
- public static String getPath(final HttpServletRequest req) {
- return req.getPathInfo();
- }
-
public static long directorySize(final Directory dir) throws IOException {
long result = 0;
for (final String name : dir.listAll()) {

0 comments on commit c11d33e

Please sign in to comment.