Encode path segments in urls to prevent illegal uris thanks to layer names with spaces.
I have a geoserver instance where users can publish layers to the geoserver. Because there are so many users that can publish to the geoserver we have been unable to prevent them from publishing layers with spaces in the names. When there are spaces (or other certain characters), the rest api is broken because the urls have spaces in them. Obviously the best solution is to fix Geoserver so that a layer with a space in the name (or in the workspace name) does not break the REST API. However that is a substantial amount of work and this change makes this library more robust in the face or inconsiderate geoserver administrators so I decided to make the change here. When I have time I would like to patch Geoserver as well but that is another issue.
This commit is contained in:
parent
7d5d159b55
commit
5588d971fc
1
pom.xml
1
pom.xml
@ -203,6 +203,7 @@
|
||||
|
||||
<properties>
|
||||
<slf4j.version>1.5.11</slf4j.version>
|
||||
<encoding>UTF-8</encoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@ -25,15 +25,6 @@
|
||||
|
||||
package it.geosolutions.geoserver.rest;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.apache.commons.httpclient.Credentials;
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
import org.apache.commons.httpclient.HttpConnectionManager;
|
||||
@ -52,6 +43,17 @@ import org.apache.commons.io.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Low level HTTP utilities.
|
||||
*/
|
||||
@ -87,8 +89,9 @@ public class HTTPUtils {
|
||||
HttpClient client = new HttpClient();
|
||||
HttpConnectionManager connectionManager = client.getHttpConnectionManager();
|
||||
try {
|
||||
setAuth(client, url, username, pw);
|
||||
httpMethod = new GetMethod(url);
|
||||
String encodedUrl = encodeUrl(url);
|
||||
setAuth(client, encodedUrl, username, pw);
|
||||
httpMethod = new GetMethod(encodedUrl);
|
||||
connectionManager.getParams().setConnectionTimeout(5000);
|
||||
int status = client.executeMethod(httpMethod);
|
||||
if (status == HttpStatus.SC_OK) {
|
||||
@ -117,6 +120,58 @@ public class HTTPUtils {
|
||||
return null;
|
||||
}
|
||||
|
||||
static String encodeUrl(String url) {
|
||||
// perform some simple encoding to the path names. This takes care of cases where
|
||||
// layers or workspaces have illegal http characters.
|
||||
// it cannot fix all
|
||||
String protocol, authority, path, query, fragment = null;
|
||||
String[] protocolPathParts = url.split("://", 2);
|
||||
if (protocolPathParts.length == 1) {
|
||||
// unexpected format so just try out url
|
||||
return url;
|
||||
}
|
||||
|
||||
protocol = protocolPathParts[0];
|
||||
String[] pathQueryParts = protocolPathParts[1].split("\\?", 2);
|
||||
path = pathQueryParts[0];
|
||||
if (pathQueryParts.length == 1) {
|
||||
query = null;
|
||||
} else {
|
||||
query = pathQueryParts[1];
|
||||
}
|
||||
|
||||
|
||||
if (query == null) {
|
||||
String[] fragmentParts = path.split("#", 2);
|
||||
if (fragmentParts.length > 1) {
|
||||
path = fragmentParts[0];
|
||||
fragment = fragmentParts[1];
|
||||
}
|
||||
} else {
|
||||
String[] fragmentParts = query.split("#", 2);
|
||||
if (fragmentParts.length > 1) {
|
||||
query = fragmentParts[0];
|
||||
fragment = fragmentParts[1];
|
||||
}
|
||||
}
|
||||
|
||||
int firstSlash = path.indexOf('/');
|
||||
if (firstSlash > -1) {
|
||||
authority = path.substring(0, firstSlash);
|
||||
path = path.substring(firstSlash);
|
||||
} else {
|
||||
authority = path;
|
||||
path = null;
|
||||
}
|
||||
|
||||
try {
|
||||
return new URI(protocol, authority, path, query, fragment).toString();
|
||||
} catch (URISyntaxException e) {
|
||||
// fallback to original string
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* PUTs a File to the given URL. <BR>
|
||||
* Basic auth is used if both username and pw are not null.
|
||||
@ -286,8 +341,9 @@ public class HTTPUtils {
|
||||
RequestEntity requestEntity, String username, String pw) {
|
||||
HttpClient client = new HttpClient();
|
||||
HttpConnectionManager connectionManager = client.getHttpConnectionManager();
|
||||
String encodedUrl = encodeUrl(url);
|
||||
try {
|
||||
setAuth(client, url, username, pw);
|
||||
setAuth(client, encodedUrl, username, pw);
|
||||
connectionManager.getParams().setConnectionTimeout(5000);
|
||||
if (requestEntity != null)
|
||||
httpMethod.setRequestEntity(requestEntity);
|
||||
@ -304,15 +360,15 @@ public class HTTPUtils {
|
||||
return response;
|
||||
default:
|
||||
LOGGER.warn("Bad response: code[" + status + "]" + " msg[" + httpMethod.getStatusText() + "]"
|
||||
+ " url[" + url + "]" + " method[" + httpMethod.getClass().getSimpleName()
|
||||
+ " url[" + encodedUrl + "]" + " method[" + httpMethod.getClass().getSimpleName()
|
||||
+ "]: " + IOUtils.toString(httpMethod.getResponseBodyAsStream()));
|
||||
return null;
|
||||
}
|
||||
} catch (ConnectException e) {
|
||||
LOGGER.info("Couldn't connect to [" + url + "]");
|
||||
LOGGER.info("Couldn't connect to [" + encodedUrl + "]");
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Error talking to " + url + " : " + e.getLocalizedMessage());
|
||||
LOGGER.error("Error talking to " + encodedUrl + " : " + e.getLocalizedMessage());
|
||||
return null;
|
||||
} finally {
|
||||
if (httpMethod != null)
|
||||
@ -326,9 +382,10 @@ public class HTTPUtils {
|
||||
DeleteMethod httpMethod = null;
|
||||
HttpClient client = new HttpClient();
|
||||
HttpConnectionManager connectionManager = client.getHttpConnectionManager();
|
||||
String encodedUrl = encodeUrl(url);
|
||||
try {
|
||||
setAuth(client, url, user, pw);
|
||||
httpMethod = new DeleteMethod(url);
|
||||
setAuth(client, encodedUrl, user, pw);
|
||||
httpMethod = new DeleteMethod(encodedUrl);
|
||||
connectionManager.getParams().setConnectionTimeout(5000);
|
||||
int status = client.executeMethod(httpMethod);
|
||||
String response = "";
|
||||
@ -343,16 +400,16 @@ public class HTTPUtils {
|
||||
return true;
|
||||
}
|
||||
if (LOGGER.isDebugEnabled())
|
||||
LOGGER.debug("(" + status + ") " + httpMethod.getStatusText() + " -- " + url);
|
||||
LOGGER.debug("(" + status + ") " + httpMethod.getStatusText() + " -- " + encodedUrl);
|
||||
return true;
|
||||
} else {
|
||||
LOGGER.info("(" + status + ") " + httpMethod.getStatusText() + " -- " + url);
|
||||
LOGGER.info("(" + status + ") " + httpMethod.getStatusText() + " -- " + encodedUrl);
|
||||
LOGGER.info("Response: '" + response + "'");
|
||||
}
|
||||
} catch (ConnectException e) {
|
||||
LOGGER.info("Couldn't connect to [" + url + "]");
|
||||
LOGGER.info("Couldn't connect to [" + encodedUrl + "]");
|
||||
} catch (IOException e) {
|
||||
LOGGER.info("Error talking to [" + url + "]", e);
|
||||
LOGGER.info("Error talking to [" + encodedUrl + "]", e);
|
||||
} finally {
|
||||
if (httpMethod != null)
|
||||
httpMethod.releaseConnection();
|
||||
@ -375,12 +432,13 @@ public class HTTPUtils {
|
||||
HttpClient client = new HttpClient();
|
||||
HttpConnectionManager connectionManager = client.getHttpConnectionManager();
|
||||
try {
|
||||
setAuth(client, url, username, pw);
|
||||
httpMethod = new GetMethod(url);
|
||||
String encodedUrl = encodeUrl(url);
|
||||
setAuth(client, encodedUrl, username, pw);
|
||||
httpMethod = new GetMethod(encodedUrl);
|
||||
connectionManager.getParams().setConnectionTimeout(2000);
|
||||
int status = client.executeMethod(httpMethod);
|
||||
if (status != HttpStatus.SC_OK) {
|
||||
LOGGER.warn("PING failed at '" + url + "': (" + status + ") " + httpMethod.getStatusText());
|
||||
LOGGER.warn("PING failed at '" + encodedUrl + "': (" + status + ") " + httpMethod.getStatusText());
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
@ -412,8 +470,9 @@ public class HTTPUtils {
|
||||
HttpClient client = new HttpClient();
|
||||
HttpConnectionManager connectionManager = client.getHttpConnectionManager();
|
||||
try {
|
||||
setAuth(client, url, username, pw);
|
||||
httpMethod = new GetMethod(url);
|
||||
String encodedUrl = encodeUrl(url);
|
||||
setAuth(client, encodedUrl, username, pw);
|
||||
httpMethod = new GetMethod(encodedUrl);
|
||||
connectionManager.getParams().setConnectionTimeout(2000);
|
||||
int status = client.executeMethod(httpMethod);
|
||||
switch (status) {
|
||||
@ -422,7 +481,7 @@ public class HTTPUtils {
|
||||
case HttpStatus.SC_NOT_FOUND:
|
||||
return false;
|
||||
default:
|
||||
throw new RuntimeException("Unhandled response status at '" + url + "': (" + status + ") "
|
||||
throw new RuntimeException("Unhandled response status at '" + encodedUrl + "': (" + status + ") "
|
||||
+ httpMethod.getStatusText());
|
||||
}
|
||||
} catch (ConnectException e) {
|
||||
@ -438,7 +497,9 @@ public class HTTPUtils {
|
||||
|
||||
private static void setAuth(HttpClient client, String url, String username, String pw)
|
||||
throws MalformedURLException {
|
||||
URL u = new URL(url);
|
||||
|
||||
String encodedUrl = encodeUrl(url);
|
||||
URL u = new URL(encodedUrl);
|
||||
if (username != null && pw != null) {
|
||||
Credentials defaultcreds = new UsernamePasswordCredentials(username, pw);
|
||||
client.getState().setCredentials(new AuthScope(u.getHost(), u.getPort()), defaultcreds);
|
||||
@ -449,7 +510,7 @@ public class HTTPUtils {
|
||||
// authentication
|
||||
} else {
|
||||
if (LOGGER.isDebugEnabled()) {
|
||||
LOGGER.debug("Not setting credentials to access to " + url);
|
||||
LOGGER.debug("Not setting credentials to access to " + encodedUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
package it.geosolutions.geoserver.rest;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class HTTPUtilsTest extends TestCase {
|
||||
public void testEncodeUrl() throws Exception {
|
||||
assertEquals("http://with%20spaces", HTTPUtils.encodeUrl("http://with spaces"));
|
||||
assertEquals("http://with%20spaces?p1=v1", HTTPUtils.encodeUrl("http://with spaces?p1=v1"));
|
||||
assertEquals("http://without/spaces?p1=v1", HTTPUtils.encodeUrl("http://without/spaces?p1=v1"));
|
||||
assertEquals("http://without/spaces", HTTPUtils.encodeUrl("http://without/spaces"));
|
||||
assertEquals("http://without/spaces#fragment", HTTPUtils.encodeUrl("http://without/spaces#fragment"));
|
||||
assertEquals("http://without/spaces?p1=v1#fragment", HTTPUtils.encodeUrl("http://without/spaces?p1=v1#fragment"));
|
||||
assertEquals("http://with%20spaces#fragment", HTTPUtils.encodeUrl("http://with spaces#fragment"));
|
||||
assertEquals("brokenurl?p1=v1", HTTPUtils.encodeUrl("brokenurl?p1=v1"));
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user