diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpClient.java b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpClient.java
new file mode 100644
index 0000000000..943d1a0352
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpClient.java
@@ -0,0 +1,189 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.IOException;
+
+/**
+ * Advanced client for http operations. The {@link AdvancedHttpClient} replaces
+ * the much more simpler implementation {@link sonia.scm.net.HttpClient}. The
+ * {@link AdvancedHttpClient} offers a fluid interface for handling most common
+ * http operations. The {@link AdvancedHttpClient} can be injected by the
+ * default injection mechanism of SCM-Manager.
+ *
+ * Http GET example:
+ *
+ *
+ * AdvancedHttpResponse response = client.get("https://www.scm-manager.org")
+ * .decodeGZip(true)
+ * .request();
+ *
+ * System.out.println(response.contentAsString());
+ *
+ *
+ *
+ * Http POST example:
+ *
+ *
+ * AdvancedHttpResponse response = client.post("https://www.scm-manager.org")
+ * .formContent()
+ * .field("firstname", "Tricia")
+ * .field("lastname", "McMillan")
+ * .build()
+ * .request();
+ *
+ * if (response.isSuccessful()){
+ * System.out.println("success");
+ * }
+ *
+ *
+ * @author Sebastian Sdorra
+ * @since 1.46
+ *
+ * @apiviz.landmark
+ */
+public abstract class AdvancedHttpClient
+{
+
+ /**
+ * Executes the given request and returns the http response. Implementation
+ * have to check, if the instance if from type
+ * {@link AdvancedHttpRequestWithBody} in order to handle request contents.
+ *
+ *
+ * @param request request to execute
+ *
+ * @return http response
+ *
+ * @throws IOException
+ */
+ protected abstract AdvancedHttpResponse request(BaseHttpRequest> request)
+ throws IOException;
+
+ /**
+ * Returns a builder for a DELETE request.
+ *
+ *
+ * @param url request url
+ *
+ * @return request builder
+ */
+ public AdvancedHttpRequestWithBody delete(String url)
+ {
+ return new AdvancedHttpRequestWithBody(this, HttpMethod.DELETE, url);
+ }
+
+ /**
+ * Returns a builder for a HEAD request.
+ *
+ *
+ * @param url request url
+ *
+ * @return request builder
+ */
+ public AdvancedHttpRequest head(String url)
+ {
+ return new AdvancedHttpRequest(this, HttpMethod.HEAD, url);
+ }
+
+ /**
+ * Returns a request builder with a custom method. Note: not
+ * every method is supported by the underlying implementation of the http
+ * client.
+ *
+ *
+ * @param method http method
+ * @param url request url
+ *
+ * @return request builder
+ */
+ public AdvancedHttpRequestWithBody method(String method, String url)
+ {
+ return new AdvancedHttpRequestWithBody(this, method, url);
+ }
+
+ /**
+ * Returns a builder for a OPTIONS request.
+ *
+ *
+ * @param url request url
+ *
+ * @return request builder
+ */
+ public AdvancedHttpRequestWithBody options(String url)
+ {
+ return new AdvancedHttpRequestWithBody(this, HttpMethod.OPTIONS, url);
+ }
+
+ /**
+ * Returns a builder for a POST request.
+ *
+ *
+ * @param url request url
+ *
+ * @return request builder
+ */
+ public AdvancedHttpRequestWithBody post(String url)
+ {
+ return new AdvancedHttpRequestWithBody(this, HttpMethod.POST, url);
+ }
+
+ /**
+ * Returns a builder for a PUT request.
+ *
+ *
+ * @param url request url
+ *
+ * @return request builder
+ */
+ public AdvancedHttpRequestWithBody put(String url)
+ {
+ return new AdvancedHttpRequestWithBody(this, HttpMethod.PUT, url);
+ }
+
+ //~--- get methods ----------------------------------------------------------
+
+ /**
+ * Returns a builder for a GET request.
+ *
+ *
+ * @param url request url
+ *
+ * @return request builder
+ */
+ public AdvancedHttpRequest get(String url)
+ {
+ return new AdvancedHttpRequest(this, HttpMethod.GET, url);
+ }
+}
diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequest.java b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequest.java
new file mode 100644
index 0000000000..b8940628b4
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequest.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+//~--- JDK imports ------------------------------------------------------------
+
+/**
+ * An http request without {@link Content} for example a GET or HEAD request.
+ *
+ * @author Sebastian Sdorra
+ * @since 1.46
+ */
+public class AdvancedHttpRequest extends BaseHttpRequest
+{
+
+ /**
+ * Constructs a new {@link AdvancedHttpRequest}
+ *
+ *
+ * @param client
+ * @param method
+ * @param url
+ */
+ AdvancedHttpRequest(AdvancedHttpClient client, String method,
+ String url)
+ {
+ super(client, method, url);
+ }
+
+ //~--- methods --------------------------------------------------------------
+
+ /**
+ * Returns {@code this}.
+ *
+ *
+ * @return {@code this}
+ */
+ @Override
+ protected AdvancedHttpRequest self()
+ {
+ return this;
+ }
+}
diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBody.java b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBody.java
new file mode 100644
index 0000000000..1acad0efb0
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBody.java
@@ -0,0 +1,216 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.common.base.Charsets;
+import com.google.common.io.ByteSource;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.File;
+
+import java.nio.charset.Charset;
+
+/**
+ * Http request with body.
+ *
+ * @author Sebastian Sdorra
+ * @since 1.46
+ */
+public class AdvancedHttpRequestWithBody
+ extends BaseHttpRequest
+{
+
+ /**
+ * Constructs a new {@link AdvancedHttpRequestWithBody}.
+ *
+ *
+ * @param client http client
+ * @param method http method
+ * @param url url
+ */
+ AdvancedHttpRequestWithBody(AdvancedHttpClient client, String method,
+ String url)
+ {
+ super(client, method, url);
+ }
+
+ //~--- methods --------------------------------------------------------------
+
+ /**
+ * Sets the content length for the request.
+ *
+ *
+ * @param length content length
+ *
+ * @return {@code this}
+ */
+ public AdvancedHttpRequestWithBody contentLength(long length)
+ {
+ return header("Content-Length", String.valueOf(length));
+ }
+
+ /**
+ * Sets the content type for the request.
+ *
+ *
+ * @param contentType content type
+ *
+ * @return {@code this}
+ */
+ public AdvancedHttpRequestWithBody contentType(String contentType)
+ {
+ return header("Content-Type", contentType);
+ }
+
+ /**
+ * Sets the content of the file as request content.
+ *
+ *
+ * @param file file
+ *
+ * @return {@code this}
+ */
+ public AdvancedHttpRequestWithBody fileContent(File file)
+ {
+ this.content = new FileContent(file);
+
+ return this;
+ }
+
+ /**
+ * Returns a {@link FormContentBuilder}. The builder can be used to add form
+ * parameters as content for the request. Note: you have to
+ * call {@link FormContentBuilder#build()} in order to apply the form content
+ * to the request.
+ *
+ * @return form content builder
+ */
+ public FormContentBuilder formContent()
+ {
+ return new FormContentBuilder(this);
+ }
+
+ /**
+ * Sets the raw data as request content.
+ *
+ *
+ * @param data raw data
+ *
+ * @return {@code this}
+ */
+ public AdvancedHttpRequestWithBody rawContent(byte[] data)
+ {
+ this.content = new RawContent(data);
+
+ return this;
+ }
+
+ /**
+ * Sets the raw data as request content.
+ *
+ *
+ * @param source byte source
+ *
+ * @return {@code this}
+ */
+ public AdvancedHttpRequestWithBody rawContent(ByteSource source)
+ {
+ this.content = new ByteSourceContent(source);
+
+ return this;
+ }
+
+ /**
+ * Sets the string as request content.
+ *
+ *
+ * @param content string content
+ *
+ * @return {@code this}
+ */
+ public AdvancedHttpRequestWithBody stringContent(String content)
+ {
+ return stringContent(content, Charsets.UTF_8);
+ }
+
+ /**
+ * Sets the string as request content.
+ *
+ *
+ * @param content string content
+ * @param charset charset of content
+ *
+ * @return {@code this}
+ */
+ public AdvancedHttpRequestWithBody stringContent(String content,
+ Charset charset)
+ {
+ this.content = new StringContent(content, charset);
+
+ return this;
+ }
+
+ //~--- get methods ----------------------------------------------------------
+
+ /**
+ * Returns the content or the request.
+ *
+ *
+ * @return request content
+ */
+ public Content getContent()
+ {
+ return content;
+ }
+
+ //~--- methods --------------------------------------------------------------
+
+ /**
+ * Returns {@code this}.
+ *
+ *
+ * @return {@code this}
+ */
+ @Override
+ protected AdvancedHttpRequestWithBody self()
+ {
+ return this;
+ }
+
+ //~--- fields ---------------------------------------------------------------
+
+ /** request content */
+ private Content content;
+}
diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpResponse.java b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpResponse.java
new file mode 100644
index 0000000000..6148b3b355
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpResponse.java
@@ -0,0 +1,199 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.common.base.Charsets;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
+import com.google.common.io.ByteSource;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Http response. The response of a {@link AdvancedHttpRequest} or
+ * {@link AdvancedHttpRequestWithBody}.
+ *
+ * @author Sebastian Sdorra
+ * @since 1.46
+ */
+public abstract class AdvancedHttpResponse
+{
+
+ //~--- methods --------------------------------------------------------------
+
+ /**
+ * Returns the content of the response as byte array.
+ *
+ *
+ * @return content as byte array
+ *
+ * @throws IOException
+ */
+ public byte[] content() throws IOException
+ {
+ ByteSource content = contentAsByteSource();
+ byte[] data = null;
+
+ if (content != null)
+ {
+ data = content.read();
+ }
+
+ return data;
+ }
+
+ /**
+ * Returns a reader for the content of the response.
+ *
+ *
+ * @return read of response content
+ *
+ * @throws IOException
+ */
+ public BufferedReader contentAsReader() throws IOException
+ {
+ ByteSource content = contentAsByteSource();
+ BufferedReader reader = null;
+ if (content != null)
+ {
+ reader = content.asCharSource(Charsets.UTF_8).openBufferedStream();
+ }
+
+ return reader;
+ }
+
+ /**
+ * Returns response content as stream.
+ *
+ *
+ * @return response content as stram
+ *
+ * @throws IOException
+ */
+ public InputStream contentAsStream() throws IOException
+ {
+ ByteSource content = contentAsByteSource();
+ InputStream stream = null;
+ if (content != null)
+ {
+ stream = content.openBufferedStream();
+ }
+
+ return stream;
+ }
+
+ /**
+ * Returns the response content as byte source.
+ *
+ *
+ * @return response content as byte source
+ * @throws IOException
+ */
+ public abstract ByteSource contentAsByteSource() throws IOException;
+
+ /**
+ * Returns the response content as string.
+ *
+ *
+ * @return response content
+ *
+ * @throws IOException
+ */
+ public String contentAsString() throws IOException
+ {
+ ByteSource content = contentAsByteSource();
+ String value = null;
+ if (content != null)
+ {
+ value = content.asCharSource(Charsets.UTF_8).read();
+ }
+
+ return value;
+ }
+
+ //~--- get methods ----------------------------------------------------------
+
+ /**
+ * Returns the first header value for the given header name or {@code null}.
+ *
+ *
+ * @param name header name
+ *
+ * @return header value or {@code null}
+ */
+ public String getFirstHeader(String name)
+ {
+ return Iterables.getFirst(getHeaders().get(name), null);
+ }
+
+ /**
+ * Returns the response headers.
+ *
+ *
+ * @return response headers
+ */
+ public abstract Multimap getHeaders();
+
+ /**
+ * Returns {@code true} if the response was successful. A response is
+ * successful, if the status code is greater than 199 and lower than 400.
+ *
+ * @return {@code true} if the response was successful
+ */
+ public boolean isSuccessful()
+ {
+ int status = getStatus();
+ return status > 199 && status < 400;
+ }
+
+ /**
+ * Returns the status code of the response.
+ *
+ *
+ * @return status code
+ */
+ public abstract int getStatus();
+
+ /**
+ * Returns the status text of the response.
+ *
+ *
+ * @return status text
+ */
+ public abstract String getStatusText();
+}
diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java b/scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java
new file mode 100644
index 0000000000..f48e819ddc
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java
@@ -0,0 +1,408 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Strings;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+
+import org.apache.shiro.codec.Base64;
+
+import sonia.scm.util.HttpUtil;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.IOException;
+
+/**
+ * Base class for http requests.
+ *
+ * @author Sebastian Sdorra
+ * @param request implementation
+ *
+ * @since 1.46
+ */
+public abstract class BaseHttpRequest
+{
+
+ /**
+ * Constructs a new {@link BaseHttpRequest}.
+ *
+ *
+ * @param client http client
+ * @param method http method
+ * @param url url
+ */
+ public BaseHttpRequest(AdvancedHttpClient client, String method, String url)
+ {
+ this.client = client;
+ this.method = method;
+ this.url = url;
+ }
+
+ //~--- methods --------------------------------------------------------------
+
+ /**
+ * Executes the request and returns the http response.
+ *
+ *
+ * @return http response
+ *
+ * @throws IOException
+ */
+ public AdvancedHttpResponse request() throws IOException
+ {
+ return client.request(this);
+ }
+
+ /**
+ * Implementing classes should return {@code this}.
+ *
+ *
+ * @return request instance
+ */
+ protected abstract T self();
+
+ /**
+ * Enabled http basic authentication.
+ *
+ *
+ * @param username username for http basic authentication
+ * @param password password for http basic authentication
+ *
+ * @return http request instance
+ */
+ public T basicAuth(String username, String password)
+ {
+ String auth = Strings.nullToEmpty(username).concat(":").concat(
+ Strings.nullToEmpty(password));
+
+ auth = Base64.encodeToString(auth.getBytes(Charsets.ISO_8859_1));
+ headers.put("Authorization", "Basic ".concat(auth));
+
+ return self();
+ }
+
+ /**
+ * Enable or disabled gzip decoding. The default value is false.
+ *
+ *
+ * @param decodeGZip true to enable gzip decoding
+ *
+ * @return request instance
+ */
+ public T decodeGZip(boolean decodeGZip)
+ {
+ this.decodeGZip = decodeGZip;
+
+ return self();
+ }
+
+ /**
+ * Enable or disable certificate validation of ssl certificates. The default
+ * value is false.
+ *
+ *
+ * @param disableCertificateValidation true to disable certificate validation
+ *
+ * @return request instance
+ */
+ public T disableCertificateValidation(boolean disableCertificateValidation)
+ {
+ this.disableCertificateValidation = disableCertificateValidation;
+
+ return self();
+ }
+
+ /**
+ * Enable or disable the validation of ssl hostnames. The default value is
+ * false.
+ *
+ *
+ * @param disableHostnameValidation true to disable ssl hostname validation
+ *
+ * @return request instance
+ */
+ public T disableHostnameValidation(boolean disableHostnameValidation)
+ {
+ this.disableHostnameValidation = disableHostnameValidation;
+
+ return self();
+ }
+
+ /**
+ * Add http headers to request.
+ *
+ *
+ * @param name header name
+ * @param values header values
+ *
+ * @return request instance
+ */
+ public T header(String name, Object... values)
+ {
+ for (Object v : values)
+ {
+ headers.put(name, toString(v));
+ }
+
+ return self();
+ }
+
+ /**
+ * Add http headers to request.
+ *
+ *
+ * @param name header name
+ * @param values header values
+ *
+ * @return request instance
+ */
+ public T headers(String name, Iterable extends Object> values)
+ {
+ for (Object v : values)
+ {
+ headers.put(name, toString(v));
+ }
+
+ return self();
+ }
+
+ /**
+ * Ignore proxy settings. The default value is false.
+ *
+ *
+ * @param ignoreProxySettings true to ignore proxy settings.
+ *
+ * @return request instance
+ */
+ public T ignoreProxySettings(boolean ignoreProxySettings)
+ {
+ this.ignoreProxySettings = ignoreProxySettings;
+
+ return self();
+ }
+
+ /**
+ * Appends a query parameter to the request.
+ *
+ *
+ * @param name name of query parameter
+ * @param values query parameter values
+ *
+ * @return request instance
+ */
+ public T queryString(String name, Object... values)
+ {
+ for (Object v : values)
+ {
+ appendQueryString(name, v);
+ }
+
+ return self();
+ }
+
+ /**
+ * Appends a query parameter to the request.
+ *
+ *
+ * @param name name of query parameter
+ * @param values query parameter values
+ *
+ * @return request instance
+ */
+ public T queryStrings(String name, Iterable extends Object> values)
+ {
+ for (Object v : values)
+ {
+ appendQueryString(name, v);
+ }
+
+ return self();
+ }
+
+ //~--- get methods ----------------------------------------------------------
+
+ /**
+ * Return a map with http headers used for the request.
+ *
+ *
+ * @return map with http headers
+ */
+ public Multimap getHeaders()
+ {
+ return headers;
+ }
+
+ /**
+ * Returns the http method for the request.
+ *
+ *
+ * @return http method of request
+ */
+ public String getMethod()
+ {
+ return method;
+ }
+
+ /**
+ * Returns the url for the request.
+ *
+ *
+ * @return url of the request
+ */
+ public String getUrl()
+ {
+ return url;
+ }
+
+ /**
+ * Returns true if the request decodes gzip compression.
+ *
+ *
+ * @return true if the request decodes gzip compression
+ */
+ public boolean isDecodeGZip()
+ {
+ return decodeGZip;
+ }
+
+ /**
+ * Returns true if the verification of ssl certificates is disabled.
+ *
+ *
+ * @return true if certificate verification is disabled
+ */
+ public boolean isDisableCertificateValidation()
+ {
+ return disableCertificateValidation;
+ }
+
+ /**
+ * Returns true if the ssl hostname validation is disabled.
+ *
+ *
+ * @return true if the ssl hostname validation is disabled
+ */
+ public boolean isDisableHostnameValidation()
+ {
+ return disableHostnameValidation;
+ }
+
+ /**
+ * Returns true if the proxy settings are ignored.
+ *
+ *
+ * @return true if the proxy settings are ignored
+ */
+ public boolean isIgnoreProxySettings()
+ {
+ return ignoreProxySettings;
+ }
+
+ //~--- methods --------------------------------------------------------------
+
+ /**
+ * Returns the value url encoded.
+ *
+ *
+ * @param value value to encode
+ *
+ * @return encoded value
+ */
+ protected String encoded(Object value)
+ {
+ return HttpUtil.encode(Strings.nullToEmpty(toString(value)));
+ }
+
+ /**
+ * Returns string representation of the given object or {@code null}, if the
+ * object is {@code null}.
+ *
+ *
+ * @param object object
+ *
+ * @return string representation or {@code null}
+ */
+ protected String toString(Object object)
+ {
+ return (object != null)
+ ? object.toString()
+ : null;
+ }
+
+ private void appendQueryString(String name, Object value)
+ {
+ StringBuilder buffer = new StringBuilder();
+
+ if (url.contains("?"))
+ {
+ buffer.append("&");
+ }
+ else
+ {
+ buffer.append("?");
+ }
+
+ buffer.append(encoded(name)).append("=").append(encoded(value));
+ url = url.concat(buffer.toString());
+ }
+
+ //~--- fields ---------------------------------------------------------------
+
+ /** http client */
+ protected final AdvancedHttpClient client;
+
+ /** http header */
+ private final Multimap headers = HashMultimap.create();
+
+ /** http method */
+ private final String method;
+
+ /** ignore proxy settings */
+ private boolean ignoreProxySettings = false;
+
+ /** disable ssl hostname validation */
+ private boolean disableHostnameValidation = false;
+
+ /** disable ssl certificate validation */
+ private boolean disableCertificateValidation = false;
+
+ /** decode gzip */
+ private boolean decodeGZip = false;
+
+ /** url of request */
+ private String url;
+}
diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/ByteSourceContent.java b/scm-core/src/main/java/sonia/scm/net/ahc/ByteSourceContent.java
new file mode 100644
index 0000000000..f4182c0975
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/ByteSourceContent.java
@@ -0,0 +1,97 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.common.io.ByteSource;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * {@link ByteSource} content for {@link AdvancedHttpRequestWithBody}.
+ *
+ * @author Sebastian Sdorra
+ * @since 1.46
+ */
+public class ByteSourceContent implements Content
+{
+
+ /**
+ * Constructs a new {@link ByteSourceContent}.
+ *
+ *
+ * @param byteSource byte source
+ */
+ public ByteSourceContent(ByteSource byteSource)
+ {
+ this.byteSource = byteSource;
+ }
+
+ //~--- methods --------------------------------------------------------------
+
+ /**
+ * Sets the content length for the request.
+ *
+ *
+ * @param request http request
+ *
+ * @throws IOException
+ */
+ @Override
+ public void prepare(AdvancedHttpRequestWithBody request) throws IOException
+ {
+ request.contentLength(byteSource.size());
+ }
+
+ /**
+ * Copies the content of the byte source to the output stream.
+ *
+ *
+ * @param output output stream
+ *
+ * @throws IOException
+ */
+ @Override
+ public void process(OutputStream output) throws IOException
+ {
+ byteSource.copyTo(output);
+ }
+
+ //~--- fields ---------------------------------------------------------------
+
+ /** byte source */
+ private final ByteSource byteSource;
+}
diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/Content.java b/scm-core/src/main/java/sonia/scm/net/ahc/Content.java
new file mode 100644
index 0000000000..0f73e7b0d6
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/Content.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Content of a {@link AdvancedHttpRequestWithBody}.
+ *
+ * @author Sebastian Sdorra
+ * @since 1.46
+ */
+public interface Content
+{
+
+ /**
+ * Prepares the {@link AdvancedHttpRequestWithBody} for the request content.
+ * Implementations can set the content type, content length or custom headers
+ * for the request.
+ *
+ *
+ * @param request request
+ *
+ * @throws IOException
+ */
+ public void prepare(AdvancedHttpRequestWithBody request) throws IOException;
+
+ /**
+ * Copies the content to the output stream.
+ *
+ *
+ * @param output output stream
+ *
+ * @throws IOException
+ */
+ public void process(OutputStream output) throws IOException;
+}
diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/FileContent.java b/scm-core/src/main/java/sonia/scm/net/ahc/FileContent.java
new file mode 100644
index 0000000000..51f1af6c21
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/FileContent.java
@@ -0,0 +1,109 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.common.io.ByteStreams;
+import com.google.common.io.Closeables;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Sets the content of the file to the request.
+ *
+ * @author Sebastian Sdorra
+ * @since 1.46
+ */
+public class FileContent implements Content
+{
+
+ /**
+ * Constructs a new {@link FileContent}.
+ *
+ *
+ * @param file file
+ */
+ public FileContent(File file)
+ {
+ this.file = file;
+ }
+
+ //~--- methods --------------------------------------------------------------
+
+ /**
+ * Sets the content length of the file as request header.
+ *
+ *
+ * @param request request
+ */
+ @Override
+ public void prepare(AdvancedHttpRequestWithBody request)
+ {
+ request.contentLength(file.length());
+ }
+
+ /**
+ * Copies the content of the file to the output stream.
+ *
+ *
+ * @param output output stream
+ *
+ * @throws IOException
+ */
+ @Override
+ public void process(OutputStream output) throws IOException
+ {
+ InputStream stream = null;
+
+ try
+ {
+ stream = new FileInputStream(file);
+ ByteStreams.copy(stream, output);
+ }
+ finally
+ {
+ Closeables.close(stream, true);
+ }
+ }
+
+ //~--- fields ---------------------------------------------------------------
+
+ /** file */
+ private final File file;
+}
diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/FormContentBuilder.java b/scm-core/src/main/java/sonia/scm/net/ahc/FormContentBuilder.java
new file mode 100644
index 0000000000..d25c12ce94
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/FormContentBuilder.java
@@ -0,0 +1,139 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.common.base.Strings;
+
+import sonia.scm.util.HttpUtil;
+
+/**
+ * The form builder is able to add form parameters to a request.
+ *
+ * @author Sebastian Sdorra
+ * @since 1.46
+ */
+public class FormContentBuilder
+{
+
+ /**
+ * Constructs a new {@link FormContentBuilder}.
+ *
+ *
+ * @param request request
+ */
+ public FormContentBuilder(AdvancedHttpRequestWithBody request)
+ {
+ this.request = request;
+ }
+
+ //~--- methods --------------------------------------------------------------
+
+ /**
+ * Build the formular content and append it to the request.
+ *
+ *
+ * @return request instance
+ */
+ public AdvancedHttpRequestWithBody build()
+ {
+ request.contentType("application/x-www-form-urlencoded");
+ request.stringContent(builder.toString());
+
+ return request;
+ }
+
+ /**
+ * Adds a formular parameter.
+ *
+ *
+ * @param name parameter name
+ * @param values parameter values
+ *
+ * @return {@code this}
+ */
+ public FormContentBuilder fields(String name, Iterable extends Object> values)
+ {
+ for (Object v : values)
+ {
+ append(name, v);
+ }
+
+ return this;
+ }
+
+ /**
+ * Adds a formular parameter.
+ *
+ *
+ * @param name parameter name
+ * @param values parameter values
+ *
+ * @return {@code this}
+ */
+ public FormContentBuilder field(String name, Object... values)
+ {
+ for (Object v : values)
+ {
+ append(name, v);
+ }
+
+ return this;
+ }
+
+ private void append(String name, Object value)
+ {
+ if (!Strings.isNullOrEmpty(name))
+ {
+ if (builder.length() > 0)
+ {
+ builder.append("&");
+ }
+
+ builder.append(HttpUtil.encode(name)).append("=");
+
+ if (value != null)
+ {
+ builder.append(HttpUtil.encode(value.toString()));
+ }
+ }
+ }
+
+ //~--- fields ---------------------------------------------------------------
+
+ /** content builder */
+ private final StringBuilder builder = new StringBuilder();
+
+ /** request */
+ private final AdvancedHttpRequestWithBody request;
+}
diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/HttpMethod.java b/scm-core/src/main/java/sonia/scm/net/ahc/HttpMethod.java
new file mode 100644
index 0000000000..41a23dcccb
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/HttpMethod.java
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+/**
+ * Http methods.
+ *
+ * @author Sebastian Sdorra
+ * @since 1.46
+ */
+public final class HttpMethod
+{
+
+ /** http delete method */
+ public static final String DELETE = "DELETE";
+
+ /** http get method */
+ public static final String GET = "GET";
+
+ /** http head method */
+ public static final String HEAD = "HEAD";
+
+ /** http options method */
+ public static final String OPTIONS = "OPTIONS";
+
+ /** http post method */
+ public static final String POST = "POST";
+
+ /** http put method */
+ public static final String PUT = "PUT";
+
+ //~--- constructors ---------------------------------------------------------
+
+ private HttpMethod() {}
+}
diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/RawContent.java b/scm-core/src/main/java/sonia/scm/net/ahc/RawContent.java
new file mode 100644
index 0000000000..0b5e99bc49
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/RawContent.java
@@ -0,0 +1,109 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.common.annotations.VisibleForTesting;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Byte array content for {@link AdvancedHttpRequestWithBody}.
+ *
+ * @author Sebastian Sdorra
+ * @since 1.46
+ */
+public class RawContent implements Content
+{
+
+ /**
+ * Constructs a new {@link RawContent}.
+ *
+ *
+ * @param data data
+ */
+ public RawContent(byte[] data)
+ {
+ this.data = data;
+ }
+
+ //~--- methods --------------------------------------------------------------
+
+ /**
+ * Sets the length of the byte array as http header.
+ *
+ *
+ * @param request request
+ */
+ @Override
+ public void prepare(AdvancedHttpRequestWithBody request)
+ {
+ request.contentLength(data.length);
+ }
+
+ /**
+ * Writes the byte array to the output stream.
+ *
+ *
+ * @param output output stream
+ *
+ * @throws IOException
+ */
+ @Override
+ public void process(OutputStream output) throws IOException
+ {
+ output.write(data);
+ }
+
+ //~--- get methods ----------------------------------------------------------
+
+ /**
+ * Returns content data.
+ *
+ *
+ * @return content data
+ */
+ @VisibleForTesting
+ byte[] getData()
+ {
+ return data;
+ }
+
+ //~--- fields ---------------------------------------------------------------
+
+ /** byte array */
+ private final byte[] data;
+}
diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/StringContent.java b/scm-core/src/main/java/sonia/scm/net/ahc/StringContent.java
new file mode 100644
index 0000000000..07473447a7
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/StringContent.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.nio.charset.Charset;
+
+/**
+ * String content for {@link AdvancedHttpRequestWithBody}.
+ *
+ * @author Sebastian Sdorra
+ * @since 1.46
+ */
+public class StringContent extends RawContent
+{
+
+ /**
+ * Constructs a new {@link StringContent}.
+ *
+ *
+ * @param content content
+ * @param charset charset
+ */
+ public StringContent(String content, Charset charset)
+ {
+ super(content.getBytes(charset));
+ }
+}
diff --git a/scm-core/src/main/java/sonia/scm/net/ahc/package-info.java b/scm-core/src/main/java/sonia/scm/net/ahc/package-info.java
new file mode 100644
index 0000000000..d11a603b5a
--- /dev/null
+++ b/scm-core/src/main/java/sonia/scm/net/ahc/package-info.java
@@ -0,0 +1,36 @@
+/**
+ * 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
+ *
+ */
+
+
+/**
+ * Advanced http client.
+ */
+package sonia.scm.net.ahc;
diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpClientTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpClientTest.java
new file mode 100644
index 0000000000..18b291d8ad
--- /dev/null
+++ b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpClientTest.java
@@ -0,0 +1,104 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+import java.io.IOException;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+public class AdvancedHttpClientTest {
+
+ private final AdvancedHttpClient client = new AdvancedHttpClient()
+ {
+
+ @Override
+ protected AdvancedHttpResponse request(
+ BaseHttpRequest> request) throws IOException
+ {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+ };
+
+ private static final String URL = "https://www.scm-manager.org";
+
+ @Test
+ public void testGet()
+ {
+ AdvancedHttpRequest request = client.get(URL);
+ assertEquals(URL, request.getUrl());
+ assertEquals(HttpMethod.GET, request.getMethod());
+ }
+
+ @Test
+ public void testDelete()
+ {
+ AdvancedHttpRequestWithBody request = client.delete(URL);
+ assertEquals(URL, request.getUrl());
+ assertEquals(HttpMethod.DELETE, request.getMethod());
+ }
+
+ @Test
+ public void testPut()
+ {
+ AdvancedHttpRequestWithBody request = client.put(URL);
+ assertEquals(URL, request.getUrl());
+ assertEquals(HttpMethod.PUT, request.getMethod());
+ }
+
+ @Test
+ public void testPost()
+ {
+ AdvancedHttpRequestWithBody request = client.post(URL);
+ assertEquals(URL, request.getUrl());
+ assertEquals(HttpMethod.POST, request.getMethod());
+ }
+
+ @Test
+ public void testOptions()
+ {
+ AdvancedHttpRequestWithBody request = client.options(URL);
+ assertEquals(URL, request.getUrl());
+ assertEquals(HttpMethod.OPTIONS, request.getMethod());
+ }
+
+ @Test
+ public void testHead()
+ {
+ AdvancedHttpRequest request = client.head(URL);
+ assertEquals(URL, request.getUrl());
+ assertEquals(HttpMethod.HEAD, request.getMethod());
+ }
+}
\ No newline at end of file
diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestTest.java
new file mode 100644
index 0000000000..5b2d675e97
--- /dev/null
+++ b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestTest.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class AdvancedHttpRequestTest {
+
+ @Mock
+ private AdvancedHttpClient ahc;
+
+ @Test
+ public void testSelf()
+ {
+ AdvancedHttpRequest ahr = new AdvancedHttpRequest(ahc, HttpMethod.GET, "https://www.scm-manager.org");
+ assertEquals(AdvancedHttpRequest.class, ahr.self().getClass());
+ }
+
+}
\ No newline at end of file
diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java
new file mode 100644
index 0000000000..8e656133e7
--- /dev/null
+++ b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java
@@ -0,0 +1,127 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.ByteSource;
+import java.io.File;
+import java.io.IOException;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.*;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class AdvancedHttpRequestWithBodyTest {
+
+ @Mock
+ private AdvancedHttpClient ahc;
+
+ private AdvancedHttpRequestWithBody request;
+
+ @Rule
+ public TemporaryFolder tempFolder = new TemporaryFolder();
+
+ @Before
+ public void before(){
+ request = new AdvancedHttpRequestWithBody(ahc, HttpMethod.PUT, "https://www.scm-manager.org");
+ }
+
+ @Test
+ public void testContentLength()
+ {
+ request.contentLength(12l);
+ assertEquals("12", request.getHeaders().get("Content-Length").iterator().next());
+ }
+
+ @Test
+ public void testContentType(){
+ request.contentType("text/plain");
+ assertEquals("text/plain", request.getHeaders().get("Content-Type").iterator().next());
+ }
+
+ @Test
+ public void testFileContent() throws IOException{
+ File file = tempFolder.newFile();
+ request.fileContent(file);
+ assertThat(request.getContent(), instanceOf(FileContent.class));
+ }
+
+ @Test
+ public void testRawContent() throws IOException {
+ request.rawContent("test".getBytes(Charsets.UTF_8));
+ assertThat(request.getContent(), instanceOf(RawContent.class));
+ }
+
+ @Test
+ public void testRawContentWithByteSource() throws IOException {
+ ByteSource bs = ByteSource.wrap("test".getBytes(Charsets.UTF_8));
+ request.rawContent(bs);
+ assertThat(request.getContent(), instanceOf(ByteSourceContent.class));
+ }
+
+ @Test
+ public void testFormContent(){
+ FormContentBuilder builder = request.formContent();
+ assertNotNull(builder);
+ builder.build();
+ assertThat(request.getContent(), instanceOf(StringContent.class));
+ }
+
+ @Test
+ public void testStringContent(){
+ request.stringContent("test");
+ assertThat(request.getContent(), instanceOf(StringContent.class));
+ }
+
+ @Test
+ public void testStringContentWithCharset(){
+ request.stringContent("test", Charsets.UTF_8);
+ assertThat(request.getContent(), instanceOf(StringContent.class));
+ }
+
+ @Test
+ public void testSelf()
+ {
+ assertEquals(AdvancedHttpRequestWithBody.class, request.self().getClass());
+ }
+
+}
\ No newline at end of file
diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpResponseTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpResponseTest.java
new file mode 100644
index 0000000000..9b3ccaa348
--- /dev/null
+++ b/scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpResponseTest.java
@@ -0,0 +1,91 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.ByteSource;
+import com.google.common.io.ByteStreams;
+import com.google.common.io.CharStreams;
+import java.io.IOException;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.Mock;
+import static org.mockito.Mockito.*;
+import org.mockito.runners.MockitoJUnitRunner;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class AdvancedHttpResponseTest {
+
+ @Mock(answer = Answers.CALLS_REAL_METHODS)
+ private AdvancedHttpResponse response;
+
+ @Test
+ public void testContent() throws IOException
+ {
+ ByteSource bs = ByteSource.wrap("test123".getBytes(Charsets.UTF_8));
+ when(response.contentAsByteSource()).thenReturn(bs);
+ byte[] data = response.content();
+ assertEquals("test123", new String(data, Charsets.UTF_8));
+ }
+
+ @Test
+ public void testContentAsString() throws IOException
+ {
+ ByteSource bs = ByteSource.wrap("123test".getBytes(Charsets.UTF_8));
+ when(response.contentAsByteSource()).thenReturn(bs);
+ assertEquals("123test", response.contentAsString());
+ }
+
+ @Test
+ public void testContentAsReader() throws IOException
+ {
+ ByteSource bs = ByteSource.wrap("abc123".getBytes(Charsets.UTF_8));
+ when(response.contentAsByteSource()).thenReturn(bs);
+ assertEquals("abc123", CharStreams.toString(response.contentAsReader()));
+ }
+
+ @Test
+ public void testContentAsStream() throws IOException
+ {
+ ByteSource bs = ByteSource.wrap("cde456".getBytes(Charsets.UTF_8));
+ when(response.contentAsByteSource()).thenReturn(bs);
+ byte[] data = ByteStreams.toByteArray(response.contentAsStream());
+ assertEquals("cde456", new String(data, Charsets.UTF_8));
+ }
+
+}
\ No newline at end of file
diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/BaseHttpRequestTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/BaseHttpRequestTest.java
new file mode 100644
index 0000000000..7adab5aa84
--- /dev/null
+++ b/scm-core/src/test/java/sonia/scm/net/ahc/BaseHttpRequestTest.java
@@ -0,0 +1,142 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import java.io.IOException;
+import java.util.Collection;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.*;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import static org.mockito.Mockito.*;
+import org.mockito.runners.MockitoJUnitRunner;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class BaseHttpRequestTest {
+
+ @Mock
+ private AdvancedHttpClient ahc;
+
+ private BaseHttpRequest request;
+
+ @Before
+ public void before(){
+ request = new AdvancedHttpRequest(ahc, HttpMethod.GET, "https://www.scm-manager.org");
+ }
+
+ @Test
+ public void testBasicAuth()
+ {
+ request.basicAuth("tricia", "mcmillian123");
+ Multimap headers = request.getHeaders();
+ assertEquals("Basic dHJpY2lhOm1jbWlsbGlhbjEyMw==", headers.get("Authorization").iterator().next());
+ }
+
+ @Test
+ public void testQueryString(){
+ request.queryString("a", "b");
+ assertEquals("https://www.scm-manager.org?a=b", request.getUrl());
+ }
+
+ @Test
+ public void testQueryStringMultiple(){
+ request.queryString("a", "b");
+ request.queryString("c", "d", "e");
+ assertEquals("https://www.scm-manager.org?a=b&c=d&c=e", request.getUrl());
+ }
+
+ @Test
+ public void testQueryStringEncoded(){
+ request.queryString("a", "äüö");
+ assertEquals("https://www.scm-manager.org?a=%C3%A4%C3%BC%C3%B6", request.getUrl());
+ }
+
+ @Test
+ public void testQueryStrings(){
+ Iterable extends Object> i1 = Lists.newArrayList("b");
+ Iterable extends Object> i2 = Lists.newArrayList("d", "e");
+ request.queryStrings("a", i1);
+ request.queryStrings("c", i2);
+ assertEquals("https://www.scm-manager.org?a=b&c=d&c=e", request.getUrl());
+ }
+
+ @Test
+ public void testQuerqStringNullValue(){
+ request.queryString("a", null, "b");
+ assertEquals("https://www.scm-manager.org?a=&a=b", request.getUrl());
+ }
+
+ @Test
+ public void testHeader(){
+ request.header("a", "b");
+ assertEquals("b", request.getHeaders().get("a").iterator().next());
+ }
+
+ @Test
+ public void testHeaderMultiple(){
+ request.header("a", "b", "c", "d");
+ Collection values = request.getHeaders().get("a");
+ assertThat(values, containsInAnyOrder("b", "c", "d"));
+ }
+
+ @Test
+ public void testRequest() throws IOException{
+ request.request();
+ verify(ahc).request(request);
+ }
+
+ @Test
+ public void testBuilderMethods(){
+ Iterable extends Object> i1 = Lists.newArrayList("b");
+ assertThat(request.decodeGZip(true), instanceOf(AdvancedHttpRequest.class));
+ assertTrue(request.isDecodeGZip());
+ assertThat(request.disableCertificateValidation(true), instanceOf(AdvancedHttpRequest.class));
+ assertTrue(request.isDisableCertificateValidation());
+ assertThat(request.disableHostnameValidation(true), instanceOf(AdvancedHttpRequest.class));
+ assertTrue(request.isDisableHostnameValidation());
+ assertThat(request.ignoreProxySettings(true), instanceOf(AdvancedHttpRequest.class));
+ assertTrue(request.isIgnoreProxySettings());
+ assertThat(request.header("a", "b"), instanceOf(AdvancedHttpRequest.class));
+ assertThat(request.headers("a", i1), instanceOf(AdvancedHttpRequest.class));
+ assertThat(request.queryString("a", "b"), instanceOf(AdvancedHttpRequest.class));
+ assertThat(request.queryStrings("a", i1), instanceOf(AdvancedHttpRequest.class));
+ }
+
+}
\ No newline at end of file
diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/ByteSourceContentTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/ByteSourceContentTest.java
new file mode 100644
index 0000000000..24487f6582
--- /dev/null
+++ b/scm-core/src/test/java/sonia/scm/net/ahc/ByteSourceContentTest.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+import com.google.common.base.Charsets;
+import com.google.common.io.ByteSource;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import static org.mockito.Mockito.*;
+import org.mockito.runners.MockitoJUnitRunner;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ByteSourceContentTest {
+
+ @Mock
+ private AdvancedHttpRequestWithBody request;
+
+ @Test
+ public void testPrepareRequest() throws IOException
+ {
+ ByteSource source = ByteSource.wrap("abc".getBytes(Charsets.UTF_8));
+ ByteSourceContent content = new ByteSourceContent(source);
+ content.prepare(request);
+ verify(request).contentLength(3l);
+ }
+
+ @Test
+ public void testProcess() throws IOException{
+ ByteSource source = ByteSource.wrap("abc".getBytes(Charsets.UTF_8));
+ ByteSourceContent content = new ByteSourceContent(source);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ content.process(baos);
+ assertEquals("abc", baos.toString("UTF-8"));
+ }
+
+}
\ No newline at end of file
diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/FileContentTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/FileContentTest.java
new file mode 100644
index 0000000000..95b381b880
--- /dev/null
+++ b/scm-core/src/test/java/sonia/scm/net/ahc/FileContentTest.java
@@ -0,0 +1,116 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.common.base.Charsets;
+import com.google.common.io.Files;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import static org.junit.Assert.*;
+
+import static org.mockito.Mockito.*;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class FileContentTest
+{
+
+ /**
+ * Method description
+ *
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testPrepareRequest() throws IOException
+ {
+ FileContent content = create("abc");
+
+ content.prepare(request);
+ verify(request).contentLength(3l);
+ }
+
+ /**
+ * Method description
+ *
+ *
+ * @throws IOException
+ */
+ @Test
+ public void testProcess() throws IOException
+ {
+ FileContent content = create("abc");
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ content.process(baos);
+ assertEquals("abc", baos.toString("UTF-8"));
+ }
+
+ private FileContent create(String value) throws IOException
+ {
+ File file = temp.newFile();
+
+ Files.write("abc", file, Charsets.UTF_8);
+
+ return new FileContent(file);
+ }
+
+ //~--- fields ---------------------------------------------------------------
+
+ /** Field description */
+ @Rule
+ public TemporaryFolder temp = new TemporaryFolder();
+
+ /** Field description */
+ @Mock
+ private AdvancedHttpRequestWithBody request;
+}
diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/FormContentBuilderTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/FormContentBuilderTest.java
new file mode 100644
index 0000000000..56cc6d7379
--- /dev/null
+++ b/scm-core/src/test/java/sonia/scm/net/ahc/FormContentBuilderTest.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+import com.google.common.collect.Lists;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import static org.mockito.Mockito.*;
+import org.mockito.runners.MockitoJUnitRunner;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class FormContentBuilderTest {
+
+ @Mock
+ private AdvancedHttpRequestWithBody request;
+
+ @InjectMocks
+ private FormContentBuilder builder;
+
+ @Test
+ public void testFieldEncoding()
+ {
+ builder.field("a", "ü", "ä", "ö").build();
+ assertContent("a=%C3%BC&a=%C3%A4&a=%C3%B6");
+ }
+
+ @Test
+ public void testBuild()
+ {
+ builder.field("a", "b").build();
+ assertContent("a=b");
+ verify(request).contentType("application/x-www-form-urlencoded");
+ }
+
+ @Test
+ public void testFieldWithArray()
+ {
+ builder.field("a", "b").field("c", "d", "e").build();
+ assertContent("a=b&c=d&c=e");
+ }
+
+ @Test
+ public void testFieldWithIterable()
+ {
+ Iterable extends Object> i1 = Lists.newArrayList("b");
+ builder.fields("a", i1)
+ .fields("c", Lists.newArrayList("d", "e"))
+ .build();
+ assertContent("a=b&c=d&c=e");
+ }
+
+ private void assertContent(String content){
+ ArgumentCaptor captor = ArgumentCaptor.forClass(String.class);
+ verify(request).stringContent(captor.capture());
+ assertEquals(content, captor.getValue());
+ }
+
+}
\ No newline at end of file
diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/RawContentTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/RawContentTest.java
new file mode 100644
index 0000000000..f4ee8b7031
--- /dev/null
+++ b/scm-core/src/test/java/sonia/scm/net/ahc/RawContentTest.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+import com.google.common.base.Charsets;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import static org.mockito.Mockito.*;
+import org.mockito.runners.MockitoJUnitRunner;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class RawContentTest {
+
+
+ @Mock
+ private AdvancedHttpRequestWithBody request;
+
+ @Test
+ public void testPrepareRequest()
+ {
+ RawContent raw = new RawContent("abc".getBytes(Charsets.UTF_8));
+ raw.prepare(request);
+ verify(request).contentLength(3);
+ }
+
+ @Test
+ public void testProcess() throws IOException
+ {
+ RawContent raw = new RawContent("abc".getBytes(Charsets.UTF_8));
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ raw.process(baos);
+ assertEquals("abc", baos.toString("UTF-8"));
+ }
+
+}
\ No newline at end of file
diff --git a/scm-core/src/test/java/sonia/scm/net/ahc/StringContentTest.java b/scm-core/src/test/java/sonia/scm/net/ahc/StringContentTest.java
new file mode 100644
index 0000000000..bd191438ca
--- /dev/null
+++ b/scm-core/src/test/java/sonia/scm/net/ahc/StringContentTest.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+import com.google.common.base.Charsets;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ */
+public class StringContentTest {
+
+
+ @Test
+ public void testStringContent()
+ {
+ StringContent sc = new StringContent("abc", Charsets.UTF_8);
+ assertEquals("abc", new String(sc.getData()));
+ }
+
+ @Test
+ public void testStringContentWithCharset()
+ {
+ StringContent sc = new StringContent("üäö", Charsets.ISO_8859_1);
+ assertEquals("üäö", new String(sc.getData(), Charsets.ISO_8859_1));
+ }
+
+}
\ No newline at end of file
diff --git a/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java
new file mode 100644
index 0000000000..1aadef143c
--- /dev/null
+++ b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java
@@ -0,0 +1,319 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Multimap;
+import com.google.common.io.Closeables;
+import com.google.inject.Inject;
+
+import org.apache.shiro.codec.Base64;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import sonia.scm.config.ScmConfiguration;
+import sonia.scm.net.Proxies;
+import sonia.scm.net.TrustAllHostnameVerifier;
+import sonia.scm.net.TrustAllTrustManager;
+import sonia.scm.util.HttpUtil;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.net.HttpURLConnection;
+import java.net.InetSocketAddress;
+import java.net.ProtocolException;
+import java.net.Proxy;
+import java.net.SocketAddress;
+import java.net.URL;
+
+import java.security.KeyManagementException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ * @since 1.46
+ */
+public class DefaultAdvancedHttpClient extends AdvancedHttpClient
+{
+
+ /** Field description */
+ public static final String CREDENTIAL_SEPARATOR = ":";
+
+ /** Field description */
+ public static final String HEADER_PROXY_AUTHORIZATION = "Proxy-Authorization";
+
+ /** Field description */
+ public static final String PREFIX_BASIC_AUTHENTICATION = "Basic ";
+
+ /** Field description */
+ public static final int TIMEOUT_CONNECTION = 30000;
+
+ /** Field description */
+ public static final int TIMEOUT_RAED = 1200000;
+
+ /**
+ * the logger for DefaultAdvancedHttpClient
+ */
+ private static final Logger logger =
+ LoggerFactory.getLogger(DefaultAdvancedHttpClient.class);
+
+ //~--- constructors ---------------------------------------------------------
+
+ /**
+ * Constructs ...
+ *
+ *
+ * @param configuration
+ */
+ @Inject
+ public DefaultAdvancedHttpClient(ScmConfiguration configuration)
+ {
+ this.configuration = configuration;
+ }
+
+ //~--- methods --------------------------------------------------------------
+
+ /**
+ * Method description
+ *
+ *
+ * @param request
+ *
+ * @return
+ *
+ * @throws IOException
+ */
+ @Override
+ protected AdvancedHttpResponse request(BaseHttpRequest> request)
+ throws IOException
+ {
+ HttpURLConnection connection = openConnection(request,
+ new URL(request.getUrl()));
+
+ applyBaseSettings(request, connection);
+
+ if (connection instanceof HttpsURLConnection)
+ {
+ applySSLSettings(request, (HttpsURLConnection) connection);
+ }
+
+ Content content = null;
+
+ if (request instanceof AdvancedHttpRequestWithBody)
+ {
+ AdvancedHttpRequestWithBody ahrwb = (AdvancedHttpRequestWithBody) request;
+
+ content = ahrwb.getContent();
+
+ if (content != null)
+ {
+ content.prepare(ahrwb);
+ }
+ else
+ {
+ request.header(HttpUtil.HEADER_CONTENT_LENGTH, "0");
+ }
+ }
+ else
+ {
+ request.header(HttpUtil.HEADER_CONTENT_LENGTH, "0");
+ }
+
+ applyHeaders(request, connection);
+
+ if (content != null)
+ {
+ applyContent(connection, content);
+ }
+
+ return new DefaultAdvancedHttpResponse(connection,
+ connection.getResponseCode(), connection.getResponseMessage());
+ }
+
+ private void appendProxyAuthentication(HttpURLConnection connection)
+ {
+ String username = configuration.getProxyUser();
+ String password = configuration.getProxyPassword();
+
+ if (!Strings.isNullOrEmpty(username) ||!Strings.isNullOrEmpty(password))
+ {
+ logger.debug("append proxy authentication header for user {}", username);
+
+ String auth = username.concat(CREDENTIAL_SEPARATOR).concat(password);
+
+ auth = Base64.encodeToString(auth.getBytes());
+ connection.addRequestProperty(HEADER_PROXY_AUTHORIZATION,
+ PREFIX_BASIC_AUTHENTICATION.concat(auth));
+ }
+ }
+
+ private void applyBaseSettings(BaseHttpRequest> request,
+ HttpURLConnection connection)
+ throws ProtocolException
+ {
+ connection.setRequestMethod(request.getMethod());
+ connection.setReadTimeout(TIMEOUT_RAED);
+ connection.setConnectTimeout(TIMEOUT_CONNECTION);
+ }
+
+ private void applyContent(HttpURLConnection connection, Content content)
+ throws IOException
+ {
+ connection.setDoOutput(true);
+
+ OutputStream output = null;
+
+ try
+ {
+ output = connection.getOutputStream();
+ content.process(output);
+ }
+ finally
+ {
+ Closeables.close(output, true);
+ }
+ }
+
+ private void applyHeaders(BaseHttpRequest> request,
+ HttpURLConnection connection)
+ {
+ Multimap headers = request.getHeaders();
+
+ for (String key : headers.keySet())
+ {
+ for (String value : headers.get(key))
+ {
+ connection.setRequestProperty(key, value);
+ }
+ }
+ }
+
+ private void applySSLSettings(BaseHttpRequest> request,
+ HttpsURLConnection connection)
+ {
+ if (request.isDisableCertificateValidation())
+ {
+ logger.trace("disable certificate validation");
+
+ try
+ {
+ TrustManager[] trustAllCerts = new TrustManager[] {
+ new TrustAllTrustManager() };
+ SSLContext sc = SSLContext.getInstance("SSL");
+
+ sc.init(null, trustAllCerts, new java.security.SecureRandom());
+ connection.setSSLSocketFactory(sc.getSocketFactory());
+ }
+ catch (KeyManagementException ex)
+ {
+ logger.error("could not disable certificate validation", ex);
+ }
+ catch (NoSuchAlgorithmException ex)
+ {
+ logger.error("could not disable certificate validation", ex);
+ }
+ }
+
+ if (request.isDisableHostnameValidation())
+ {
+ logger.trace("disable hostname validation");
+ connection.setHostnameVerifier(new TrustAllHostnameVerifier());
+ }
+ }
+
+ private HttpURLConnection openConnection(BaseHttpRequest> request, URL url)
+ throws IOException
+ {
+ HttpURLConnection connection;
+
+ if (isProxyEnabled(request))
+ {
+ connection = openProxyConnection(request, url);
+ appendProxyAuthentication(connection);
+ }
+ else
+ {
+ if (request.isIgnoreProxySettings())
+ {
+ logger.trace("ignore proxy settings");
+ }
+
+ logger.debug("fetch {}", url.toExternalForm());
+
+ connection = (HttpURLConnection) url.openConnection();
+ }
+
+ return connection;
+ }
+
+ private HttpURLConnection openProxyConnection(BaseHttpRequest> request,
+ URL url)
+ throws IOException
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("fetch '{}' using proxy {}:{}", url.toExternalForm(),
+ configuration.getProxyServer(), configuration.getProxyPort());
+ }
+
+ SocketAddress address =
+ new InetSocketAddress(configuration.getProxyServer(),
+ configuration.getProxyPort());
+
+ return (HttpURLConnection) url.openConnection(new Proxy(Proxy.Type.HTTP,
+ address));
+ }
+
+ //~--- get methods ----------------------------------------------------------
+
+ private boolean isProxyEnabled(BaseHttpRequest> request)
+ {
+ return !request.isIgnoreProxySettings()
+ && Proxies.isEnabled(configuration, request.getUrl());
+ }
+
+ //~--- fields ---------------------------------------------------------------
+
+ /** Field description */
+ private final ScmConfiguration configuration;
+}
diff --git a/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java
new file mode 100644
index 0000000000..a679995a44
--- /dev/null
+++ b/scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java
@@ -0,0 +1,194 @@
+/**
+ * Copyright (c) 2014, 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.ahc;
+
+//~--- non-JDK imports --------------------------------------------------------
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.io.ByteSource;
+
+//~--- JDK imports ------------------------------------------------------------
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import java.net.HttpURLConnection;
+
+import java.util.List;
+import java.util.Map.Entry;
+
+/**
+ *
+ * @author Sebastian Sdorra
+ * @since 1.46
+ */
+public class DefaultAdvancedHttpResponse extends AdvancedHttpResponse
+{
+
+ /**
+ * Constructs ...
+ *
+ *
+ * @param connection
+ * @param status
+ * @param statusText
+ */
+ DefaultAdvancedHttpResponse(HttpURLConnection connection, int status,
+ String statusText)
+ {
+ this.connection = connection;
+ this.status = status;
+ this.statusText = statusText;
+ }
+
+ //~--- methods --------------------------------------------------------------
+
+ /**
+ * Method description
+ *
+ *
+ * @return
+ *
+ * @throws IOException
+ */
+ @Override
+ public ByteSource contentAsByteSource() throws IOException
+ {
+ return new URLConnectionByteSource(connection);
+ }
+
+ //~--- get methods ----------------------------------------------------------
+
+ /**
+ * Method description
+ *
+ *
+ * @return
+ */
+ @Override
+ public Multimap getHeaders()
+ {
+ if (headers == null)
+ {
+ headers = HashMultimap.create();
+
+ for (Entry> e :
+ connection.getHeaderFields().entrySet())
+ {
+ headers.putAll(e.getKey(), e.getValue());
+ }
+ }
+
+ return headers;
+ }
+
+ /**
+ * Method description
+ *
+ *
+ * @return
+ */
+ @Override
+ public int getStatus()
+ {
+ return status;
+ }
+
+ /**
+ * Method description
+ *
+ *
+ * @return
+ */
+ @Override
+ public String getStatusText()
+ {
+ return statusText;
+ }
+
+ //~--- inner classes --------------------------------------------------------
+
+ /**
+ * {@link ByteSource} implementation of a http connection.
+ */
+ private static class URLConnectionByteSource extends ByteSource
+ {
+
+ /**
+ * Constructs a new {@link URLConnectionByteSource}.
+ *
+ *
+ * @param connection http connection
+ */
+ private URLConnectionByteSource(HttpURLConnection connection)
+ {
+ this.connection = connection;
+ }
+
+ //~--- methods ------------------------------------------------------------
+
+ /**
+ * Opens the http connection.
+ *
+ *
+ * @return http connection
+ *
+ * @throws IOException
+ */
+ @Override
+ public InputStream openStream() throws IOException
+ {
+ return connection.getInputStream();
+ }
+
+ //~--- fields -------------------------------------------------------------
+
+ /** http connection */
+ private final HttpURLConnection connection;
+ }
+
+
+ //~--- fields ---------------------------------------------------------------
+
+ /** http connection */
+ private final HttpURLConnection connection;
+
+ /** Field description */
+ private final int status;
+
+ /** Field description */
+ private final String statusText;
+
+ /** http headers */
+ private Multimap headers;
+}