From 3d46ffc7e45cbd8b732a1003f063a569ab670915 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Mon, 9 May 2011 12:03:58 +0200 Subject: [PATCH] added HttpClient api to fix proxy problems, see #14 --- .../sonia/scm/config/ScmConfiguration.java | 78 ++++++ .../main/java/sonia/scm/net/HttpClient.java | 92 +++++++ .../main/java/sonia/scm/net/HttpResponse.java | 99 +++++++ .../java/sonia/scm/net/URLHttpClient.java | 258 ++++++++++++++++++ .../java/sonia/scm/net/URLHttpResponse.java | 211 ++++++++++++++ .../scm/plugin/DefaultPluginManager.java | 1 + 6 files changed, 739 insertions(+) create mode 100644 scm-core/src/main/java/sonia/scm/net/HttpClient.java create mode 100644 scm-core/src/main/java/sonia/scm/net/HttpResponse.java create mode 100644 scm-webapp/src/main/java/sonia/scm/net/URLHttpClient.java create mode 100644 scm-webapp/src/main/java/sonia/scm/net/URLHttpResponse.java diff --git a/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java b/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java index c9b079df5b..5ae5793e37 100644 --- a/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java +++ b/scm-core/src/main/java/sonia/scm/config/ScmConfiguration.java @@ -96,6 +96,9 @@ public class ScmConfiguration this.anonymousAccessEnabled = other.anonymousAccessEnabled; this.adminUsers = other.adminUsers; this.adminGroups = other.adminGroups; + this.enableProxy = other.enableProxy; + this.proxyPort = other.proxyPort; + this.proxyServer = other.proxyServer; } //~--- get methods ---------------------------------------------------------- @@ -155,6 +158,28 @@ public class ScmConfiguration return pluginUrl; } + /** + * Method description + * + * + * @return + */ + public int getProxyPort() + { + return proxyPort; + } + + /** + * Method description + * + * + * @return + */ + public String getProxyServer() + { + return proxyServer; + } + /** * Method description * @@ -199,6 +224,17 @@ public class ScmConfiguration return enablePortForward; } + /** + * Method description + * + * + * @return + */ + public boolean isEnableProxy() + { + return enableProxy; + } + /** * Method description * @@ -267,6 +303,17 @@ public class ScmConfiguration this.enablePortForward = enablePortForward; } + /** + * Method description + * + * + * @param enableProxy + */ + public void setEnableProxy(boolean enableProxy) + { + this.enableProxy = enableProxy; + } + /** * Method description * @@ -300,6 +347,28 @@ public class ScmConfiguration this.pluginUrl = pluginUrl; } + /** + * Method description + * + * + * @param proxyPort + */ + public void setProxyPort(int proxyPort) + { + this.proxyPort = proxyPort; + } + + /** + * Method description + * + * + * @param proxyServer + */ + public void setProxyServer(String proxyServer) + { + this.proxyServer = proxyServer; + } + /** * Method description * @@ -334,6 +403,9 @@ public class ScmConfiguration @XmlJavaTypeAdapter(XmlSetStringAdapter.class) private Set adminUsers; + /** Field description */ + private boolean enableProxy; + /** Field description */ private int forwardPort = 80; @@ -341,6 +413,12 @@ public class ScmConfiguration @XmlElement(name = "plugin-url") private String pluginUrl = DEFAULT_PLUGINURL; + /** Field description */ + private int proxyPort; + + /** Field description */ + private String proxyServer; + /** Field description */ private String servername = "localhost"; diff --git a/scm-core/src/main/java/sonia/scm/net/HttpClient.java b/scm-core/src/main/java/sonia/scm/net/HttpClient.java new file mode 100644 index 0000000000..cf27201c68 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/net/HttpClient.java @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.net; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * + * @author Sebastian Sdorra + */ +public interface HttpClient +{ + + /** + * Method description + * + * + * @param url + * + * @return + */ + public HttpResponse post(String url) throws IOException; + + /** + * Method description + * + * + * @param url + * @param parameters + * + * @return + */ + public HttpResponse post(String url, Map> parameters) throws IOException; + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param url + * + * @return + */ + public HttpResponse get(String url) throws IOException; + + /** + * Method description + * + * + * @param url + * @param parameters + * + * @return + */ + public HttpResponse get(String url, Map> parameters) throws IOException; +} diff --git a/scm-core/src/main/java/sonia/scm/net/HttpResponse.java b/scm-core/src/main/java/sonia/scm/net/HttpResponse.java new file mode 100644 index 0000000000..00745615c1 --- /dev/null +++ b/scm-core/src/main/java/sonia/scm/net/HttpResponse.java @@ -0,0 +1,99 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.net; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; + +import java.util.List; +import java.util.Map; + +/** + * + * @author Sebastian Sdorra + */ +public interface HttpResponse extends Closeable +{ + + /** + * Method description + * + * + * @return + * + * @throws IOException + */ + public InputStream getContent() throws IOException; + + /** + * Method description + * + * + * @return + * + * @throws IOException + */ + public String getContentAsString() throws IOException; + + /** + * Method description + * + * + * @param name + * + * @return + */ + public String getHeader(String name); + + /** + * Method description + * + * + * @return + */ + public Map> getHeaderMap(); + + /** + * Method description + * + * + * @return + * + * @throws IOException + */ + public int getStatusCode() throws IOException; +} diff --git a/scm-webapp/src/main/java/sonia/scm/net/URLHttpClient.java b/scm-webapp/src/main/java/sonia/scm/net/URLHttpClient.java new file mode 100644 index 0000000000..0edc168153 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/net/URLHttpClient.java @@ -0,0 +1,258 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.net; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.inject.Inject; + +import sonia.scm.config.ScmConfiguration; +import sonia.scm.util.Util; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; + +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.SocketAddress; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; + +import java.util.List; +import java.util.Map; + +/** + * + * @author Sebastian Sdorra + */ +public class URLHttpClient implements HttpClient +{ + + /** Field description */ + public static final String ENCODING = "UTF-8"; + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param configuration + */ + @Inject + public URLHttpClient(ScmConfiguration configuration) + { + this.configuration = configuration; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param url + * + * @return + * + * @throws IOException + */ + @Override + public HttpResponse post(String url) throws IOException + { + throw new UnsupportedOperationException("Not supported yet."); + } + + /** + * Method description + * + * + * @param url + * @param parameters + * + * @return + * + * @throws IOException + */ + @Override + public HttpResponse post(String url, Map> parameters) + throws IOException + { + throw new UnsupportedOperationException("Not supported yet."); + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param spec + * + * @return + * + * @throws IOException + */ + @Override + public HttpResponse get(String spec) throws IOException + { + return new URLHttpResponse(openConnection(spec)); + } + + /** + * Method description + * + * + * @param url + * @param parameters + * + * @return + * + * @throws IOException + */ + @Override + public HttpResponse get(String url, Map> parameters) + throws IOException + { + if (Util.isNotEmpty(parameters)) + { + StringBuilder ub = new StringBuilder(url); + boolean first = url.contains("?"); + + for (Map.Entry> p : parameters.entrySet()) + { + String key = encode(p.getKey()); + List values = p.getValue(); + + if (Util.isNotEmpty(values)) + { + for (String value : values) + { + if (first) + { + ub.append("?"); + first = false; + } + else + { + ub.append("&"); + } + + ub.append(key).append("=").append(encode(value)); + } + } + } + + url = ub.toString(); + } + + return new URLHttpResponse(openConnection(url)); + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param param + * + * @return + */ + private String encode(String param) + { + try + { + param = URLEncoder.encode(param, ENCODING); + } + catch (IOException ex) + { + throw new RuntimeException(ex); + } + + return param; + } + + /** + * Method description + * + * + * @param spec + * + * @return + * + * @throws IOException + */ + private URLConnection openConnection(String spec) throws IOException + { + return openConnection(new URL(spec)); + } + + /** + * Method description + * + * + * @param url + * + * @return + * + * @throws IOException + */ + private URLConnection openConnection(URL url) throws IOException + { + URLConnection connection = null; + + if (configuration.isEnableProxy()) + { + SocketAddress address = + new InetSocketAddress(configuration.getProxyServer(), + configuration.getProxyPort()); + + connection = url.openConnection(new Proxy(Proxy.Type.HTTP, address)); + } + else + { + connection = url.openConnection(); + } + + return connection; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private ScmConfiguration configuration; +} diff --git a/scm-webapp/src/main/java/sonia/scm/net/URLHttpResponse.java b/scm-webapp/src/main/java/sonia/scm/net/URLHttpResponse.java new file mode 100644 index 0000000000..c793502e4f --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/net/URLHttpResponse.java @@ -0,0 +1,211 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.net; + +//~--- non-JDK imports -------------------------------------------------------- + +import sonia.scm.util.IOUtil; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import java.net.HttpURLConnection; +import java.net.URLConnection; + +import java.util.List; +import java.util.Map; + +/** + * + * @author Sebastian Sdorra + */ +public class URLHttpResponse implements HttpResponse +{ + + /** + * Constructs ... + * + * + * @param connection + */ + public URLHttpResponse(URLConnection connection) + { + this.connection = connection; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @throws IOException + */ + @Override + public void close() throws IOException + { + if ((connection != null) &&!clientClose) + { + InputStream in = getContent(); + + if (in != null) + { + in.close(); + } + } + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + * + * @throws IOException + */ + @Override + public InputStream getContent() throws IOException + { + clientClose = true; + + return connection.getInputStream(); + } + + /** + * Method description + * + * + * @return + * + * @throws IOException + */ + @Override + public String getContentAsString() throws IOException + { + String result = null; + InputStream in = null; + ByteArrayOutputStream baos = null; + + try + { + in = getContent(); + baos = new ByteArrayOutputStream(); + IOUtil.copy(in, baos); + baos.flush(); + + String enc = connection.getContentEncoding(); + + if (enc == null) + { + enc = "UTF-8"; + } + + result = new String(baos.toByteArray(), enc); + } + finally + { + if (in != null) + { + in.close(); + } + + if (baos != null) + { + baos.close(); + } + } + + return result; + } + + /** + * Method description + * + * + * @param name + * + * @return + */ + @Override + public String getHeader(String name) + { + return connection.getHeaderField(name); + } + + /** + * Method description + * + * + * @return + */ + @Override + public Map> getHeaderMap() + { + return connection.getHeaderFields(); + } + + /** + * Method description + * + * + * @return + * + * @throws IOException + */ + @Override + public int getStatusCode() throws IOException + { + int result = -1; + + if (connection instanceof HttpURLConnection) + { + result = ((HttpURLConnection) connection).getResponseCode(); + } + + return result; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private boolean clientClose = false; + + /** Field description */ + private URLConnection connection; +} diff --git a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java index 779edfebb0..c103239ccd 100644 --- a/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java +++ b/scm-webapp/src/main/java/sonia/scm/plugin/DefaultPluginManager.java @@ -533,6 +533,7 @@ public class DefaultPluginManager implements PluginManager try { + URLConnection connection = new URL(pluginUrl).openConnection(); input = connection.getInputStream();