Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encode path segments in urls to prevent illegal uris #124

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@

<properties>
<slf4j.version>1.5.11</slf4j.version>
<encoding>UTF-8</encoding>
</properties>

<dependencies>
Expand Down
155 changes: 108 additions & 47 deletions src/main/java/it/geosolutions/geoserver/rest/HTTPUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -52,6 +43,17 @@
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.
*/
Expand All @@ -60,7 +62,7 @@ public class HTTPUtils {

/**
* Performs an HTTP GET on the given URL.
*
*
* @param url The URL where to connect to.
* @return The HTTP response as a String if the HTTP response code was 200
* (OK).
Expand All @@ -73,7 +75,7 @@ public static String get(String url) throws MalformedURLException {
/**
* Performs an HTTP GET on the given URL. <BR>
* Basic auth is used if both username and pw are not null.
*
*
* @param url The URL where to connect to.
* @param username Basic auth credential. No basic auth if null.
* @param pw Basic auth credential. No basic auth if null.
Expand All @@ -87,8 +89,9 @@ public static String get(String url, String username, String pw) {
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) {
Expand Down Expand Up @@ -117,10 +120,62 @@ public static String get(String url, String username, String pw) {
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.
*
*
* @param url The URL where to connect to.
* @param file The File to be sent.
* @param contentType The content-type to advert in the PUT.
Expand All @@ -138,7 +193,7 @@ public static String put(String url, File file, String contentType, String usern
/**
* PUTs a String to the given URL. <BR>
* Basic auth is used if both username and pw are not null.
*
*
* @param url The URL where to connect to.
* @param content The content to be sent as a String.
* @param contentType The content-type to advert in the PUT.
Expand All @@ -161,7 +216,7 @@ public static String put(String url, String content, String contentType, String
/**
* PUTs a String representing an XML document to the given URL. <BR>
* Basic auth is used if both username and pw are not null.
*
*
* @param url The URL where to connect to.
* @param content The XML content to be sent as a String.
* @param username Basic auth credential. No basic auth if null.
Expand All @@ -178,7 +233,7 @@ public static String putXml(String url, String content, String username, String
/**
* Performs a PUT to the given URL. <BR>
* Basic auth is used if both username and pw are not null.
*
*
* @param url The URL where to connect to.
* @param requestEntity The request to be sent.
* @param username Basic auth credential. No basic auth if null.
Expand All @@ -195,7 +250,7 @@ public static String put(String url, RequestEntity requestEntity, String usernam
/**
* POSTs a File to the given URL. <BR>
* Basic auth is used if both username and pw are not null.
*
*
* @param url The URL where to connect to.
* @param file The File to be sent.
* @param contentType The content-type to advert in the POST.
Expand All @@ -213,7 +268,7 @@ public static String post(String url, File file, String contentType, String user
/**
* POSTs a String to the given URL. <BR>
* Basic auth is used if both username and pw are not null.
*
*
* @param url The URL where to connect to.
* @param content The content to be sent as a String.
* @param contentType The content-type to advert in the POST.
Expand All @@ -236,7 +291,7 @@ public static String post(String url, String content, String contentType, String
/**
* POSTs a String representing an XML document to the given URL. <BR>
* Basic auth is used if both username and pw are not null.
*
*
* @param url The URL where to connect to.
* @param content The XML content to be sent as a String.
* @param username Basic auth credential. No basic auth if null.
Expand All @@ -253,7 +308,7 @@ public static String postXml(String url, String content, String username, String
/**
* Performs a POST to the given URL. <BR>
* Basic auth is used if both username and pw are not null.
*
*
* @param url The URL where to connect to.
* @param requestEntity The request to be sent.
* @param username Basic auth credential. No basic auth if null.
Expand All @@ -279,15 +334,16 @@ public static String post(String url, RequestEntity requestEntity, String userna
* </UL>
* are accepted as successful codes; in these cases the response string will
* be returned.
*
*
* @return the HTTP response or <TT>null</TT> on errors.
*/
private static String send(final EntityEnclosingMethod httpMethod, String url,
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);
Expand All @@ -304,15 +360,15 @@ private static String send(final EntityEnclosingMethod httpMethod, String url,
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)
Expand All @@ -326,33 +382,34 @@ public static boolean delete(String url, final String user, final String pw) {
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 = "";
if (status == HttpStatus.SC_OK) {
InputStream is = httpMethod.getResponseBodyAsStream();
response = IOUtils.toString(is);
IOUtils.closeQuietly(is);
if (response.trim().equals("")) {
if (response.trim().equals("")) {
if (LOGGER.isDebugEnabled())
LOGGER
.debug("ResponseBody is empty (this may be not an error since we just performed a DELETE call)");
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();
Expand All @@ -375,12 +432,13 @@ public static boolean httpPing(String url, String username, String pw) {
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;
Expand All @@ -399,7 +457,7 @@ public static boolean httpPing(String url, String username, String pw) {

/**
* Used to query for REST resources.
*
*
* @param url The URL of the REST resource to query about.
* @param username
* @param pw
Expand All @@ -412,8 +470,9 @@ public static boolean exists(String url, String username, String pw) {
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) {
Expand All @@ -422,7 +481,7 @@ public static boolean exists(String url, String username, String pw) {
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) {
Expand All @@ -438,7 +497,9 @@ public static boolean exists(String url, String username, String pw) {

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);
Expand All @@ -449,22 +510,22 @@ private static void setAuth(HttpClient client, String url, String username, Stri
// authentication
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Not setting credentials to access to " + url);
LOGGER.debug("Not setting credentials to access to " + encodedUrl);
}
}
}

/**
* @param geoserverURL
* @return recursively remove ending slashes
* @return recursively remove ending slashes
*/
public static String decurtSlash(String geoserverURL) {
if (geoserverURL!=null && geoserverURL.endsWith("/")) {
geoserverURL = decurtSlash(geoserverURL.substring(0, geoserverURL.length() - 1));
}
return geoserverURL;
}

/**
* @param str a string array
* @return create a StringBuilder appending all the passed arguments
Expand All @@ -473,15 +534,15 @@ public static StringBuilder append(String ... str){
if (str==null){
return null;
}

StringBuilder buf=new StringBuilder();
for (String s: str){
if (s!=null)
buf.append(s);
}
return buf;
}

/**
* Wrapper for {@link #append(String...)}
* @param base base URL
Expand All @@ -492,7 +553,7 @@ public static StringBuilder append(URL base, String ... str){
if (str==null){
return append(base.toString());
}

StringBuilder buf=new StringBuilder(base.toString());
for (String s: str){
if (s!=null)
Expand Down
Loading