From 4407c7ce9ec70a1e912e6c43bbebe861faf5cd65 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Thu, 30 Apr 2015 07:17:52 +0200 Subject: [PATCH] start implementation of a new advanced http client, in order to fix issue #709 --- .../sonia/scm/net/ahc/AdvancedHttpClient.java | 189 ++++++++ .../scm/net/ahc/AdvancedHttpRequest.java | 72 ++++ .../net/ahc/AdvancedHttpRequestWithBody.java | 216 ++++++++++ .../scm/net/ahc/AdvancedHttpResponse.java | 199 +++++++++ .../sonia/scm/net/ahc/BaseHttpRequest.java | 408 ++++++++++++++++++ .../sonia/scm/net/ahc/ByteSourceContent.java | 97 +++++ .../main/java/sonia/scm/net/ahc/Content.java | 69 +++ .../java/sonia/scm/net/ahc/FileContent.java | 109 +++++ .../sonia/scm/net/ahc/FormContentBuilder.java | 139 ++++++ .../java/sonia/scm/net/ahc/HttpMethod.java | 64 +++ .../java/sonia/scm/net/ahc/RawContent.java | 109 +++++ .../java/sonia/scm/net/ahc/StringContent.java | 58 +++ .../java/sonia/scm/net/ahc/package-info.java | 36 ++ .../scm/net/ahc/AdvancedHttpClientTest.java | 104 +++++ .../scm/net/ahc/AdvancedHttpRequestTest.java | 57 +++ .../ahc/AdvancedHttpRequestWithBodyTest.java | 127 ++++++ .../scm/net/ahc/AdvancedHttpResponseTest.java | 91 ++++ .../scm/net/ahc/BaseHttpRequestTest.java | 142 ++++++ .../scm/net/ahc/ByteSourceContentTest.java | 74 ++++ .../sonia/scm/net/ahc/FileContentTest.java | 116 +++++ .../scm/net/ahc/FormContentBuilderTest.java | 95 ++++ .../sonia/scm/net/ahc/RawContentTest.java | 72 ++++ .../sonia/scm/net/ahc/StringContentTest.java | 59 +++ .../net/ahc/DefaultAdvancedHttpClient.java | 319 ++++++++++++++ .../net/ahc/DefaultAdvancedHttpResponse.java | 194 +++++++++ 25 files changed, 3215 insertions(+) create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpClient.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequest.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBody.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/AdvancedHttpResponse.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/BaseHttpRequest.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/ByteSourceContent.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/Content.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/FileContent.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/FormContentBuilder.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/HttpMethod.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/RawContent.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/StringContent.java create mode 100644 scm-core/src/main/java/sonia/scm/net/ahc/package-info.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpClientTest.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestTest.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpRequestWithBodyTest.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/AdvancedHttpResponseTest.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/BaseHttpRequestTest.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/ByteSourceContentTest.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/FileContentTest.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/FormContentBuilderTest.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/RawContentTest.java create mode 100644 scm-core/src/test/java/sonia/scm/net/ahc/StringContentTest.java create mode 100644 scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpClient.java create mode 100644 scm-webapp/src/main/java/sonia/scm/net/ahc/DefaultAdvancedHttpResponse.java 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 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 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 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 i1 = Lists.newArrayList("b"); + Iterable 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 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 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; +}