diff --git a/src/main/java/org/takes/rs/RsWithHeaders.java b/src/main/java/org/takes/rs/RsWithHeaders.java
index 5f91c943d..5d72bb7b1 100644
--- a/src/main/java/org/takes/rs/RsWithHeaders.java
+++ b/src/main/java/org/takes/rs/RsWithHeaders.java
@@ -43,6 +43,14 @@
@EqualsAndHashCode(callSuper = true)
public final class RsWithHeaders extends RsWrap {
+ /**
+ * Ctor.
+ * @param headers Headers
+ */
+ public RsWithHeaders(final Iterable extends CharSequence> headers) {
+ this(new RsEmpty(), headers);
+ }
+
/**
* Ctor.
* @param res Original response
diff --git a/src/main/java/org/takes/tk/TkProxy.java b/src/main/java/org/takes/tk/TkProxy.java
index 2cee606ae..31b554d18 100644
--- a/src/main/java/org/takes/tk/TkProxy.java
+++ b/src/main/java/org/takes/tk/TkProxy.java
@@ -23,124 +23,135 @@
*/
package org.takes.tk;
+import com.google.common.net.HostAndPort;
+import com.jcabi.http.request.ApacheRequest;
import java.io.IOException;
-import lombok.EqualsAndHashCode;
-import lombok.ToString;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
import org.takes.Request;
import org.takes.Response;
import org.takes.Take;
+import org.takes.rq.RqHeaders;
+import org.takes.rq.RqHref;
+import org.takes.rq.RqLengthAware;
+import org.takes.rq.RqMethod;
+import org.takes.rs.RsWithBody;
+import org.takes.rs.RsWithHeaders;
+import org.takes.rs.RsWithStatus;
/**
- * Proxy take.
- *
- * This take may transform the request before passing it
- * to the original take and/or transform the response received
- * from the original take.
- *
- *
If the request transformer is not provided, the original
- * request is passed to the original take.
- *
- *
If the response transformer is not provided, the response
- * received from the original take is returned.
+ * Take that proxies requests to another destination.
*
*
The class is immutable and thread-safe.
*
* @author Dragan Bozanovic (bozanovicdr@gmail.com)
* @version $Id$
* @since 0.25
+ * @checkstyle ClassDataAbstractionCouplingCheck (500 lines)
+ * @todo #377 We need the integration test for this class.
+ * The test should verify the different HTTP methods (GET, POST, etc),
+ * as well as the different combinations of request/response headers.
*/
-@ToString
-@EqualsAndHashCode(of = { "origin", "rqtransformer", "rstransformer" })
-public final class TkProxy implements Take {
-
- /**
- * Original take.
- */
- private final transient Take origin;
+public class TkProxy implements Take {
/**
- * Request transformer.
+ * Target host to which requests are forwarded.
*/
- private final transient RqTransformer rqtransformer;
-
- /**
- * Response transformer.
- */
- private final transient RsTransformer rstransformer;
+ private final transient HostAndPort host;
/**
* Ctor.
- * @param take Original take
- * @param rqtransformr Request transformer
- * @param rstransformr Response transformer
+ * @param target Target to which requests are forwarded
*/
- public TkProxy(final Take take, final RqTransformer rqtransformr,
- final RsTransformer rstransformr) {
- this.origin = take;
- this.rqtransformer = rqtransformr;
- this.rstransformer = rstransformr;
+ public TkProxy(final HostAndPort target) {
+ this.host = target;
}
- /**
- * Ctor.
- * @param take Original take
- * @param rqtransformr Request transformer
- */
- public TkProxy(final Take take, final RqTransformer rqtransformr) {
- this(take, rqtransformr, new RsTransformer() {
- @Override
- public Response transform(final Response response) {
- return response;
+ @Override
+ public final Response act(final Request req) throws IOException {
+ final RqHref.Base base = new RqHref.Base(req);
+ final String home = new RqHref.Smart(base).home().bare();
+ final String dest = StringUtils.replace(
+ base.href().toString(),
+ home,
+ String.format("http://%s/", this.host.toString())
+ );
+ final String method = new RqMethod.Base(req).method();
+ com.jcabi.http.Request proxied = new ApacheRequest(dest)
+ .method(method);
+ final RqHeaders.Base headers = new RqHeaders.Base(req);
+ for (final String name : headers.names()) {
+ if ("content-length".equals(name.toLowerCase(Locale.ENGLISH))) {
+ continue;
+ }
+ if (isHost(name)) {
+ proxied = proxied.header(name, this.host.toString());
+ continue;
}
- });
+ for (final String value : headers.header(name)) {
+ proxied = proxied.header(name, value);
+ }
+ }
+ if (Arrays.asList("POST", "PUT").contains(method)) {
+ proxied = proxied.body()
+ .set(IOUtils.toByteArray(new RqLengthAware(req).body()))
+ .back();
+ }
+ return this.response(home, dest, proxied.fetch());
}
/**
- * Ctor.
- * @param take Original take
- * @param rstransformr Response transformer
+ * Creates the response received from the target host.
+ *
+ * @param home Home host
+ * @param dest Destination URL
+ * @param rsp Response received from the target host
+ * @return Response
*/
- public TkProxy(final Take take, final RsTransformer rstransformr) {
- this(take, new RqTransformer() {
- @Override
- public Request transform(final Request request) {
- return request;
- }
- }, rstransformr);
- }
-
- @Override
- public Response act(final Request req) throws IOException {
- return this.rstransformer.transform(
- this.origin.act(
- this.rqtransformer.transform(req)
+ private Response response(final String home, final String dest,
+ final com.jcabi.http.Response rsp) {
+ final Collection hdrs = new LinkedList();
+ hdrs.add(
+ String.format(
+ "X-Takes-TkProxy: from %s to %s",
+ home, dest
)
);
+ for (final Map.Entry> entry
+ : rsp.headers().entrySet()) {
+ for (final String value : entry.getValue()) {
+ final String val;
+ if (isHost(entry.getKey())) {
+ val = this.host.toString();
+ } else {
+ val = value;
+ }
+ hdrs.add(String.format("%s: %s", entry.getKey(), val));
+ }
+ }
+ return new RsWithStatus(
+ new RsWithBody(
+ new RsWithHeaders(hdrs),
+ rsp.binary()
+ ),
+ rsp.status(),
+ rsp.reason()
+ );
}
/**
- * Request transformer.
- */
- public interface RqTransformer {
-
- /**
- * Transforms the original request.
- * @param request Original request
- * @return The transformed request
- */
- Request transform(Request request);
- }
-
- /**
- * Response transformer.
+ * Checks whether the provided argument is a "Host" header name.
+ * @param header Header name
+ * @return Returns {@code true} if {@code header} parameter is a "Host"
+ * header name, {@code false} otherwise
*/
- public interface RsTransformer {
-
- /**
- * Transforms the original response.
- * @param response Original response
- * @return Transformed response
- */
- Response transform(Response response);
+ private static boolean isHost(final String header) {
+ return "host".equals(header.toLowerCase(Locale.ENGLISH));
}
}
diff --git a/src/test/java/org/takes/tk/TkProxyTest.java b/src/test/java/org/takes/tk/TkProxyTest.java
deleted file mode 100644
index 0337f31e0..000000000
--- a/src/test/java/org/takes/tk/TkProxyTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/**
- * The MIT License (MIT)
- *
- * Copyright (c) 2015 Yegor Bugayenko
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-package org.takes.tk;
-
-import java.io.IOException;
-import org.hamcrest.MatcherAssert;
-import org.hamcrest.Matchers;
-import org.junit.Test;
-import org.mockito.Mockito;
-import org.takes.Request;
-import org.takes.Response;
-import org.takes.Take;
-import org.takes.rq.RqFake;
-import org.takes.rs.RsText;
-import org.takes.tk.TkProxy.RqTransformer;
-import org.takes.tk.TkProxy.RsTransformer;
-
-/**
- * Test case for {@link org.takes.tk.TkProxy1}.
- * @author Dragan Bozanovic (bozanovicdr@gmail.com)
- * @version $Id$
- * @since 0.25
- */
-public final class TkProxyTest {
-
- /**
- * TkProxy can proxy requests.
- * @throws IOException If some problem inside
- */
- @Test
- public void proxiesRequest() throws IOException {
- final Take take = Mockito.mock(Take.class);
- final Request transformed = new RqFake("transformed request");
- final Response response = new RsText("response");
- Mockito.when(take.act(transformed)).thenReturn(response);
- final TkProxy tkProxy = new TkProxy(
- take,
- new RqTransformer() {
- @Override
- public Request transform(final Request request) {
- return transformed;
- }
- }
- );
- MatcherAssert.assertThat(
- tkProxy.act(new RqFake()),
- Matchers.equalTo(response)
- );
- Mockito.verify(take).act(transformed);
- }
-
- /**
- * TkProxy can proxy responses.
- * @throws IOException If some problem inside
- */
- @Test
- public void proxiesResponse() throws IOException {
- final Take take = Mockito.mock(Take.class);
- final Request request = new RqFake("request");
- final Response transformed = new RsText("transformed response");
- final TkProxy tkProxy = new TkProxy(
- take,
- new RsTransformer() {
- @Override
- public Response transform(final Response response) {
- return transformed;
- }
- }
- );
- MatcherAssert.assertThat(
- tkProxy.act(request),
- Matchers.equalTo(transformed)
- );
- Mockito.verify(take).act(request);
- }
-}