Skip to content

Commit

Permalink
Merge branch '__rultor'
Browse files Browse the repository at this point in the history
  • Loading branch information
rultor committed Mar 21, 2021
2 parents 46d4a37 + 5fca01c commit 925e1a2
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 106 deletions.
31 changes: 16 additions & 15 deletions src/main/java/org/takes/rq/RequestOf.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,30 @@
import org.cactoos.scalar.And;
import org.cactoos.scalar.Equality;
import org.cactoos.scalar.HashCode;
import org.cactoos.scalar.IoChecked;
import org.cactoos.scalar.Or;
import org.cactoos.scalar.Unchecked;
import org.takes.Body;
import org.takes.Head;
import org.takes.Request;

/**
* This {@link Request} implementation provides a way to build a request
* with custom {@link Scalar} head and body values.
* This {@link Request} implementation provides a way to build a request with
* custom {@link Scalar} head and body values.
*
* <p>The class is immutable and thread-safe.
* <p>
* The class is immutable and thread-safe.
* @since 2.0
*/
public final class RequestOf implements Request {
/**
* Original head scalar.
*/
private final IoChecked<Iterable<String>> shead;
private final Head shead;

/**
* Original body scalar.
*/
private final IoChecked<InputStream> sbody;
private final Body sbody;

/**
* Ctor.
Expand All @@ -66,23 +68,22 @@ public RequestOf(final Iterable<String> head, final InputStream body) {

/**
* Ctor.
* @param head Scalar to provide head value
* @param body Scalar to provide body value
* @param head Head value
* @param body Body value
*/
public RequestOf(
final Scalar<Iterable<String>> head, final Scalar<InputStream> body) {
this.shead = new IoChecked<>(head);
this.sbody = new IoChecked<>(body);
public RequestOf(final Head head, final Body body) {
this.shead = head;
this.sbody = body;
}

@Override
public Iterable<String> head() throws IOException {
return this.shead.value();
return this.shead.head();
}

@Override
public InputStream body() throws IOException {
return this.sbody.value();
return this.sbody.body();
}

@Override
Expand Down Expand Up @@ -118,6 +119,6 @@ public boolean equals(final Object that) {

@Override
public int hashCode() {
return new HashCode(new Unchecked<>(this.shead::value).value()).value();
return new HashCode(new Unchecked<>(this.shead::head).value()).value();
}
}
21 changes: 12 additions & 9 deletions src/main/java/org/takes/rq/RqOnce.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import lombok.EqualsAndHashCode;
import org.cactoos.Scalar;
import org.cactoos.io.InputStreamOf;
import org.cactoos.scalar.IoChecked;
import org.cactoos.scalar.Sticky;
import org.cactoos.text.TextOf;
import org.takes.Request;
Expand All @@ -51,17 +52,19 @@ public final class RqOnce extends RqWrap {
public RqOnce(final Request req) {
super(
new RequestOf(
new Sticky<>(req::head),
new Scalar<InputStream>() {
private final Scalar<String> text = new Sticky<>(
new TextOf(req::body)::asString
);
new IoChecked<>(new Sticky<>(req::head))::value,
new IoChecked<>(
new Scalar<InputStream>() {
private final Scalar<String> text = new Sticky<>(
new TextOf(req::body)::asString
);

@Override
public InputStream value() throws Exception {
return new InputStreamOf(this.text.value());
@Override
public InputStream value() throws Exception {
return new InputStreamOf(this.text.value());
}
}
}
)::value
)
);
}
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/org/takes/rq/multipart/RqMtBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ public final class RqMtBase implements RqMultipart {
* Ctor.
* @param req Original request
* @throws IOException If fails
* @todo #950:30m Remove code from this ctor, leaving only
* initialization. Currently this constructor access body
* of the request and triggers its evaluation. This breaks
* composition of multiple request, as it can be seen in
* {@link RqMtFake}. When this task is done, remove
* explicit lazy evaluation for RqMtFake.
* @checkstyle ExecutableStatementCountCheck (2 lines)
*/
public RqMtBase(final Request req) throws IOException {
Expand Down
165 changes: 93 additions & 72 deletions src/main/java/org/takes/rq/multipart/RqMtFake.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,21 @@

import java.io.IOException;
import java.io.InputStream;
import org.cactoos.Scalar;
import org.cactoos.io.InputOf;
import org.cactoos.io.InputStreamOf;
import org.cactoos.scalar.IoChecked;
import org.cactoos.scalar.LengthOf;
import org.cactoos.scalar.Sticky;
import org.cactoos.scalar.Unchecked;
import org.takes.Body;
import org.takes.Request;
import org.takes.rq.RequestOf;
import org.takes.rq.RqHeaders;
import org.takes.rq.RqMultipart;
import org.takes.rq.RqPrint;
import org.takes.rq.RqWithHeaders;
import org.takes.rq.RqWrap;

/**
* Fake decorator.
Expand All @@ -50,120 +59,132 @@ public final class RqMtFake implements RqMultipart {
/**
* Fake multipart request.
*/
private final RqMultipart fake;
private final Scalar<RqMultipart> fake;

/**
* Fake ctor.
* @param req Fake request header holder
* @param dispositions Fake request body parts
* @throws IOException If fails
*/
public RqMtFake(final Request req, final Request... dispositions)
throws IOException {
this.fake = new RqMtBase(
new RqMtFake.FakeMultipartRequest(req, dispositions)
public RqMtFake(final Request req, final Request... dispositions) {
this.fake = new Sticky<>(
() -> new RqMtBase(
new RqMtFake.FakeMultipartRequest(req, dispositions)
)
);
}

@Override
public Iterable<Request> part(final CharSequence name) {
return this.fake.part(name);
return new Unchecked<>(this.fake).value().part(name);
}

@Override
public Iterable<String> names() {
return this.fake.names();
return new Unchecked<>(this.fake).value().names();
}

@Override
public Iterable<String> head() throws IOException {
return this.fake.head();
return new IoChecked<>(this.fake).value().head();
}

@Override
public InputStream body() throws IOException {
return this.fake.body();
return new IoChecked<>(this.fake).value().body();
}

/**
* Fake body creator.
* @param parts Fake request body parts
* @return StringBuilder of given dispositions
* @throws IOException If fails
*/
@SuppressWarnings(
{
"PMD.InsufficientStringBufferDeclaration",
"PMD.AvoidInstantiatingObjectsInLoops"
})
private static StringBuilder fakeBody(final Request... parts)
throws IOException {
final StringBuilder builder = new StringBuilder();
for (final Request part : parts) {
builder.append(String.format("--%s", RqMtFake.BOUNDARY))
.append(RqMtFake.CRLF)
.append("Content-Disposition: ")
.append(
new RqHeaders.Smart(
new RqHeaders.Base(part)
).single("Content-Disposition")
).append(RqMtFake.CRLF);
final String body = new RqPrint(part).printBody();
if (!(RqMtFake.CRLF.equals(body) || body.isEmpty())) {
builder.append(RqMtFake.CRLF)
.append(body)
.append(RqMtFake.CRLF);
}
}
builder.append("Content-Transfer-Encoding: utf-8")
.append(RqMtFake.CRLF)
.append(String.format("--%s--", RqMtFake.BOUNDARY));
return builder;
}

/**
* This class is using a decorator pattern for representing
* a fake HTTP multipart request.
* This class is using a decorator pattern for representing a fake HTTP
* multipart request.
* @since 0.33
*/
private static final class FakeMultipartRequest implements Request {
/**
* Request object. Holds a value for the header.
*/
private final Request req;

private static final class FakeMultipartRequest extends RqWrap {
/**
* Holding multiple request body parts.
* Ctor.
* @param rqst The Request object
* @param list The sequence of dispositions
* @throws IOException if can't process requests
*/
private final String parts;
FakeMultipartRequest(final Request rqst, final Request... list)
throws IOException {
this(rqst, new RqMtFake.FakeBody(list));
}

/**
* The Constructor for the class.
* Ctor.
* @param rqst The Request object
* @param list The sequence of dispositions
* @param body The body of dispositions
* @throws IOException if can't process requests
*/
FakeMultipartRequest(final Request rqst, final Request... list)
FakeMultipartRequest(final Request rqst, final Body body)
throws IOException {
this.req = rqst;
this.parts = RqMtFake.fakeBody(list).toString();
super(
new RequestOf(
new RqWithHeaders(
rqst,
String.format(
"Content-Type: multipart/form-data; boundary=%s",
RqMtFake.BOUNDARY
),
String.format(
"Content-Length: %s",
new LengthOf(new InputOf(body.body())).intValue()
)
),
body
)
);
}
}

@Override
public Iterable<String> head() throws IOException {
return new RqWithHeaders(
this.req,
String.format(
"Content-Type: multipart/form-data; boundary=%s",
RqMtFake.BOUNDARY
),
String.format("Content-Length: %s", this.parts.length())
).head();
/**
* Fake body .
* @since 0.33
*/
private static final class FakeBody implements Body {
/**
* The content.
*/
private final Scalar<String> content;

/**
* Ctor.
*
* @param parts The Body parts.
*/
private FakeBody(final Request... parts) {
this.content = new Sticky<>(
() -> {
final StringBuilder builder = new StringBuilder(128);
for (final Request part : parts) {
builder.append(String.format("--%s", RqMtFake.BOUNDARY))
.append(RqMtFake.CRLF)
.append("Content-Disposition: ")
.append(
new RqHeaders.Smart(
new RqHeaders.Base(part)
).single("Content-Disposition")
).append(RqMtFake.CRLF);
final String body = new RqPrint(part).printBody();
if (!(RqMtFake.CRLF.equals(body) || body.isEmpty())) {
builder.append(RqMtFake.CRLF)
.append(body)
.append(RqMtFake.CRLF);
}
}
builder.append("Content-Transfer-Encoding: utf-8")
.append(RqMtFake.CRLF)
.append(String.format("--%s--", RqMtFake.BOUNDARY));
return builder.toString();
}
);
}

@Override
public InputStream body() {
return new InputStreamOf(this.parts);
public InputStream body() throws IOException {
return new InputStreamOf(this.content::value);
}
}
}
11 changes: 1 addition & 10 deletions src/test/java/org/takes/rq/multipart/RqMtFakeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import org.hamcrest.Matchers;
import org.hamcrest.core.IsInstanceOf;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.takes.rq.RqFake;
import org.takes.rq.RqHeaders;
Expand Down Expand Up @@ -75,7 +74,7 @@ public void throwsExceptionOnNoNameAtContentDispositionHeader()
RqMtFakeTest.CONTENT_DISP, "fake=\"t-3\""
).asString()
)
);
).body();
}

/**
Expand Down Expand Up @@ -364,20 +363,12 @@ public void returnsCorrectNamesSet() throws Exception {
}
}

/*
* @todo #577:30min The test below fails with the message
* "header "Content-Disposition" is mandatory".
* The error disappears when `new RqFake(new ListOf<>(""), "")` is
* changed to `new RqFake(new ListOf<>(""), "someValue")`.
* Fix the underlying problem and remove the `@Ignore` annotation.
* */
/**
* Tests the bug described in #577.
*
* @throws Exception If there is some error inside
*/
@Test
@Ignore("See puzzle above")
public void contentDispositionShouldBeRecognized() throws Exception {
new RqMtFake(
new RqFake(),
Expand Down

2 comments on commit 925e1a2

@0pdd
Copy link
Collaborator

@0pdd 0pdd commented on 925e1a2 Mar 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Puzzle 577-dcc6f7a1 disappeared from src/test/java/org/takes/rq/multipart/RqMtFakeTest.java, that's why I closed #950. Please, remember that the puzzle was not necessarily removed in this particular commit. Maybe it happened earlier, but we discovered this fact only now.

@0pdd
Copy link
Collaborator

@0pdd 0pdd commented on 925e1a2 Mar 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Puzzle 950-0b2d9fd8 discovered in src/main/java/org/takes/rq/multipart/RqMtBase.java and submitted as #1095. Please, remember that the puzzle was not necessarily added in this particular commit. Maybe it was added earlier, but we discovered it only now.

Please sign in to comment.