Merge pull request #7007 from JLLeitschuh/feat/JLL/improve_ratpack_support

Java: Ratpack HTTP Framework Additional Modeling
This commit is contained in:
Chris Smowton
2021-11-29 16:20:53 +00:00
committed by GitHub
10 changed files with 925 additions and 8 deletions

View File

@@ -0,0 +1,308 @@
import ratpack.exec.Promise;
import ratpack.exec.Result;
import ratpack.func.Action;
import ratpack.func.Pair;
public class PairTest {
void sink(Object o) {}
String taint() {
return null;
}
void test1() {
Pair<String, String> pair = Pair.of("safe", "safe");
sink(pair.left); // no taint flow
sink(pair.left()); // no taint flow
sink(pair.getLeft()); // no taint flow
sink(pair.right); // no taint flow
sink(pair.right()); // no taint flow
sink(pair.getRight()); // no taint flow
Pair<String, String> updatedLeftPair = pair.left(taint());
sink(updatedLeftPair.left); //$hasTaintFlow
sink(updatedLeftPair.left()); //$hasTaintFlow
sink(updatedLeftPair.getLeft()); //$hasTaintFlow
sink(updatedLeftPair.right); // no taint flow
sink(updatedLeftPair.right()); // no taint flow
sink(updatedLeftPair.getRight()); // no taint flow
Pair<String, String> updatedRightPair = pair.right(taint());
sink(updatedRightPair.left); // no taint flow
sink(updatedRightPair.left()); // no taint flow
sink(updatedRightPair.getLeft()); // no taint flow
sink(updatedRightPair.right); //$hasTaintFlow
sink(updatedRightPair.right()); //$hasTaintFlow
sink(updatedRightPair.getRight()); //$hasTaintFlow
Pair<String, String> updatedBothPair = pair.left(taint()).right(taint());
sink(updatedBothPair.left); //$hasTaintFlow
sink(updatedBothPair.left()); //$hasTaintFlow
sink(updatedBothPair.getLeft()); //$hasTaintFlow
sink(updatedBothPair.right); //$hasTaintFlow
sink(updatedBothPair.right()); //$hasTaintFlow
sink(updatedBothPair.getRight()); //$hasTaintFlow
}
void test2() {
Pair<String, String> pair = Pair.of(taint(), taint());
sink(pair.left); //$hasTaintFlow
sink(pair.left()); //$hasTaintFlow
sink(pair.getLeft()); //$hasTaintFlow
sink(pair.right); //$hasTaintFlow
sink(pair.right()); //$hasTaintFlow
sink(pair.getRight()); //$hasTaintFlow
Pair<String, Pair<String, String>> pushedLeftPair = pair.pushLeft("safe");
sink(pushedLeftPair.left()); // no taint flow
sink(pushedLeftPair.right().left()); //$hasTaintFlow
sink(pushedLeftPair.right().right()); //$hasTaintFlow
Pair<Pair<String, String>, String> pushedRightPair = pair.pushRight("safe");
sink(pushedRightPair.left().left()); //$hasTaintFlow
sink(pushedRightPair.left().right()); //$hasTaintFlow
sink(pushedRightPair.right()); // no taint flow
}
void test3() {
Pair<String, String> pair = Pair.of("safe", "safe");
sink(pair.left); // no taint flow
sink(pair.left()); // no taint flow
sink(pair.getLeft()); // no taint flow
sink(pair.right); // no taint flow
sink(pair.right()); // no taint flow
sink(pair.getRight()); // no taint flow
Pair<String, Pair<String, String>> pushedLeftPair = pair.pushLeft(taint());
sink(pushedLeftPair.left()); //$hasTaintFlow
sink(pushedLeftPair.right().left()); // no taint flow
sink(pushedLeftPair.right().right()); // no taint flow
Pair<Pair<String, String>, String> pushedRightPair = pair.pushRight(taint());
sink(pushedRightPair.left().left()); // no taint flow
sink(pushedRightPair.left().right()); // no taint flow
sink(pushedRightPair.right()); //$hasTaintFlow
}
void test4() {
Pair<String, String> pair = Pair.of(taint(), taint());
sink(pair.left()); //$hasTaintFlow
sink(pair.right()); //$hasTaintFlow
Pair<Pair<String, String>, String> nestLeftPair = pair.nestLeft("safe");
sink(nestLeftPair.left().left()); // no taint flow
sink(nestLeftPair.left().right()); //$hasTaintFlow
sink(nestLeftPair.right()); //$hasTaintFlow
Pair<String, Pair<String, String>> nestRightPair = pair.nestRight("safe");
sink(nestRightPair.left()); //$hasTaintFlow
sink(nestRightPair.right().left()); // no taint flow
sink(nestRightPair.right().right()); //$hasTaintFlow
}
void test5() {
Pair<String, String> pair = Pair.of(taint(), "safe");
sink(pair.left()); //$hasTaintFlow
sink(pair.right()); // no taint flow
Pair<Pair<String, String>, String> nestLeftPair = pair.nestLeft("safe");
sink(nestLeftPair.left().left()); // no taint flow
sink(nestLeftPair.left().right()); //$hasTaintFlow
sink(nestLeftPair.right()); // no taint flow
Pair<String, Pair<String, String>> nestRightPair = pair.nestRight("safe");
sink(nestRightPair.left()); //$hasTaintFlow
sink(nestRightPair.right().left()); // no taint flow
sink(nestRightPair.right().right()); // no taint flow
}
void test6() {
Pair<String, String> pair = Pair.of("safe", taint());
sink(pair.left()); // no taint flow
sink(pair.right()); //$hasTaintFlow
Pair<Pair<String, String>, String> nestLeftPair = pair.nestLeft("safe");
sink(nestLeftPair.left().left()); // no taint flow
sink(nestLeftPair.left().right()); // no taint flow
sink(nestLeftPair.right()); //$hasTaintFlow
Pair<String, Pair<String, String>> nestRightPair = pair.nestRight("safe");
sink(nestRightPair.left()); // no taint flow
sink(nestRightPair.right().left()); // no taint flow
sink(nestRightPair.right().right()); //$hasTaintFlow
}
void test7() {
Pair<String, String> pair = Pair.of("safe", "safe");
sink(pair.left()); // no taint flow
sink(pair.right()); // no taint flow
Pair<Pair<String, String>, String> nestLeftPair = pair.nestLeft(taint());
sink(nestLeftPair.left().left()); // $hasTaintFlow
sink(nestLeftPair.left().right()); // no taint flow
sink(nestLeftPair.right()); // no taint flow
Pair<String, Pair<String, String>> nestRightPair = pair.nestRight(taint());
sink(nestRightPair.left()); // no taint flow
sink(nestRightPair.right().left()); // $hasTaintFlow
sink(nestRightPair.right().right()); // no taint flow
}
void test8() throws Exception {
Pair<String, String> pair = Pair.of("safe", "safe");
Pair<String, String> taintLeft = pair.mapLeft(left -> {
sink(left); // no taint flow
return taint();
});
sink(taintLeft.left()); //$hasTaintFlow
sink(taintLeft.right()); // no taint flow
}
void test9() throws Exception {
Pair<String, String> pair = Pair.of("safe", "safe");
Pair<String, String> taintRight = pair.mapRight(left -> {
sink(left); // no taint flow
return taint();
});
sink(taintRight.left()); // no taint flow
sink(taintRight.right()); //$hasTaintFlow
}
void test10() throws Exception {
Pair<String, String> pair = Pair.of(taint(), taint());
Pair<String, String> taintLeft = pair.mapLeft(left -> {
sink(left); //$hasTaintFlow
return "safe";
});
sink(taintLeft.left()); // no taint flow
sink(taintLeft.right()); //$hasTaintFlow
}
void test11() throws Exception {
Pair<String, String> pair = Pair.of(taint(), taint());
Pair<String, String> taintRight = pair.mapRight(right -> {
sink(right); //$hasTaintFlow
return "safe";
});
sink(taintRight.left()); //$hasTaintFlow
sink(taintRight.right()); // no taint flow
}
void test12() throws Exception {
Pair<String, String> pair = Pair.of(taint(), taint());
String safe = pair.map(p -> {
sink(p.left()); //$hasTaintFlow
sink(p.right()); //$hasTaintFlow
return "safe";
});
sink(safe); // no taint flow
String unsafe = pair.map(p -> {
sink(p.left()); //$hasTaintFlow
sink(p.right()); //$hasTaintFlow
return taint();
});
sink(unsafe); //$hasTaintFlow
}
void test13() {
Promise
.value(taint())
.left(Promise.value("safe"))
.then(pair -> {
sink(pair.left()); // no taint flow
sink(pair.right()); //$hasTaintFlow
});
Promise
.value(taint())
.right(Promise.value("safe"))
.then(pair -> {
sink(pair.left()); //$hasTaintFlow
sink(pair.right()); // no taint flow
});
Promise
.value("safe")
.left(Promise.value(taint()))
.then(pair -> {
sink(pair.left()); //$hasTaintFlow
sink(pair.right()); // no taint flow
});
Promise
.value("safe")
.right(Promise.value(taint()))
.then(pair -> {
sink(pair.left()); // no taint flow
sink(pair.right()); //$hasTaintFlow
});
}
void test14() {
Promise
.value(taint())
.left(value -> {
sink(value); //$hasTaintFlow
return "safe";
})
.then(pair -> {
sink(pair.left()); // no taint flow
sink(pair.right()); //$hasTaintFlow
});
Promise
.value(taint())
.right(value -> {
sink(value); //$hasTaintFlow
return "safe";
})
.then(pair -> {
sink(pair.left()); //$hasTaintFlow
sink(pair.right()); // no taint flow
});
Promise
.value("safe")
.left(value -> {
sink(value); // no taint flow
return taint();
})
.then(pair -> {
sink(pair.left()); //$hasTaintFlow
sink(pair.right()); // no taint flow
});
Promise
.value("safe")
.right(value -> {
sink(value); // no taint flow
return taint();
})
.then(pair -> {
sink(pair.left()); // no taint flow
sink(pair.right()); //$hasTaintFlow
});
}
void test15() {
Promise
.value(taint())
.flatLeft(value -> {
sink(value); //$hasTaintFlow
return Promise.value("safe");
})
.then(pair -> {
sink(pair.left()); // no taint flow
sink(pair.right()); //$hasTaintFlow
});
Promise
.value(taint())
.flatRight(value -> {
sink(value); //$hasTaintFlow
return Promise.value("safe");
})
.then(pair -> {
sink(pair.left()); //$hasTaintFlow
sink(pair.right()); // no taint flow
});
Promise
.value("safe")
.flatLeft(value -> {
return Promise.value(taint());
})
.then(pair -> {
sink(pair.left()); //$hasTaintFlow
sink(pair.right()); // no taint flow
});
Promise
.value("safe")
.flatRight(value -> {
return Promise.value(taint());
})
.then(pair -> {
sink(pair.left()); // no taint flow
sink(pair.right()); //$hasTaintFlow
});
}
}

View File

@@ -3,7 +3,9 @@ import ratpack.core.http.TypedData;
import ratpack.core.form.Form;
import ratpack.core.form.UploadedFile;
import ratpack.core.parse.Parse;
import ratpack.exec.Operation;
import ratpack.exec.Promise;
import ratpack.exec.Result;
import ratpack.func.Action;
import ratpack.func.Function;
import java.io.OutputStream;
@@ -167,6 +169,14 @@ class Resource {
.next(value -> {
sink(value); //$hasTaintFlow
})
.map(value -> {
sink(value); //$hasTaintFlow
return value;
})
.blockingMap(value -> {
sink(value); //$hasTaintFlow
return value;
})
.then(value -> {
sink(value); //$hasTaintFlow
});
@@ -316,5 +326,77 @@ class Resource {
.then(value -> {
sink(value); // no tainted flow
});
}
}
void test13() {
String tainted = taint();
Promise
.value(tainted)
.replace(Promise.value("safe"))
.then(value -> {
sink(value); // no tainted flow
});
Promise
.value("safe")
.replace(Promise.value(tainted))
.then(value -> {
sink(value); //$hasTaintFlow
});
}
void test14() {
String tainted = taint();
Promise
.value(tainted)
.blockingOp(value -> {
sink(value); //$hasTaintFlow
})
.then(value -> {
sink(value); //$hasTaintFlow
});
}
void test15() {
String tainted = taint();
Promise
.value(tainted)
.nextOp(value -> Operation.of(() -> {
sink(value); //$hasTaintFlow
}))
.nextOpIf(value -> {
sink(value); //$hasTaintFlow
return true;
}, value -> Operation.of(() -> {
sink(value); //$hasTaintFlow
}))
.then(value -> {
sink(value); //$hasTaintFlow
});
}
void test16() {
String tainted = taint();
Promise
.value(tainted)
.flatOp(value -> Operation.of(() -> {
sink(value); //$hasTaintFlow
}));
}
void test17() throws Exception {
String tainted = taint();
Result<String> result = Result.success(tainted);
sink(result.getValue()); //$hasTaintFlow
sink(result.getValueOrThrow()); //$hasTaintFlow
Promise
.value(tainted)
.wiretap(r -> {
sink(r.getValue()); //$hasTaintFlow
sink(r.getValueOrThrow()); //$hasTaintFlow
})
.then(value -> {
sink(value); //$hasTaintFlow
});
}
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright 2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ratpack.exec;
import ratpack.exec.api.NonBlocking;
import ratpack.func.Action;
import ratpack.func.Block;
import ratpack.func.Factory;
import ratpack.func.Function;
import ratpack.func.Predicate;
import java.util.Optional;
/**
* A logical operation.
* <p>
* An operation encapsulates a logical piece of work, which will complete some time in the future.
* It is similar to a {@link Promise} except that it does not produce a value.
* It merely succeeds, or throws an exception.
* <p>
* The {@link #then(Block)} method allows specifying what should happen after the operation completes.
* The {@link #onError(Action)} method allows specifying what should happen if the operation fails.
* Like {@link Promise}, the operation will not start until it is subscribed to, via {@link #then(Block)} or {@link #then()}.
* <p>
* It is common for methods that would naturally return {@code void} to return an {@link Operation} instead,
* to allow the method implementation to be effectively asynchronous.
* The caller of the method is then expected to use the {@link #then(Block)} method to specify what should happen after the operation
* that the method represents finishes.
* <pre class="java">{@code
* import ratpack.exec.Blocking;
* import ratpack.exec.Operation;
* import com.google.common.collect.Lists;
* import ratpack.test.exec.ExecHarness;
*
* import java.util.Arrays;
* import java.util.List;
*
* import static org.junit.Assert.assertEquals;
*
* public class Example {
* public static void main(String... args) throws Exception {
* List<String> events = Lists.newArrayList();
* ExecHarness.runSingle(e ->
* Operation.of(() ->
* Blocking.get(() -> events.add("1"))
* .then(b -> events.add("2"))
* )
* .then(() -> events.add("3"))
* );
* assertEquals(Arrays.asList("1", "2", "3"), events);
* }
* }
* }</pre>
*/
public interface Operation {
static Operation of(Block block) {
return null;
}
static Operation flatten(Factory<Operation> factory) {
return null;
}
default Operation onError(Action<? super Throwable> onError) {
return null;
}
Operation onError(Predicate<? super Throwable> predicate, Action<? super Throwable> errorHandler);
default <E extends Throwable> Operation onError(Class<E> errorType, Action<? super E> errorHandler) {
return null;
}
default Operation mapError(Action<? super Throwable> action) {
return null;
}
@NonBlocking
void then(Block block);
@NonBlocking
default void then() {
// empty
}
Promise<Void> promise();
default <T> Promise<T> map(Factory<? extends T> factory) {
return null;
}
default <T> Promise<T> flatMap(Factory<? extends Promise<T>> factory) {
return null;
}
default <T> Promise<T> flatMap(Promise<T> promise) {
return null;
}
default Operation next(Operation operation) {
return null;
}
default Operation next(Block operation) {
return null;
}
default Operation blockingNext(Block operation) {
return null;
}
default <O> O to(Function<? super Operation, ? extends O> function) throws Exception {
return null;
}
default Operation wiretap(Action<? super Optional<? extends Throwable>> action) {
return null;
}
static Operation noop() {
return null;
}
}

View File

@@ -19,6 +19,7 @@ package ratpack.exec;
import ratpack.func.Action;
import ratpack.func.Factory;
import ratpack.func.Function;
import ratpack.func.Pair;
import ratpack.func.Predicate;
/**
@@ -66,6 +67,14 @@ public interface Promise<T> {
Promise<T> next(Action<? super T> action);
default Promise<T> nextOp(Function<? super T, ? extends Operation> function) {
return null;
}
default Promise<T> nextOpIf(Predicate<? super T> predicate, Function<? super T, ? extends Operation> function) {
return null;
}
default <E extends Throwable> Promise<T> onError(Class<E> errorType, Action<? super E> errorHandler) {
return null;
}
@@ -82,6 +91,46 @@ public interface Promise<T> {
return null;
}
default <O> Promise<O> blockingMap(Function<? super T, ? extends O> transformer) {
return null;
}
default Promise<T> blockingOp(Action<? super T> action) {
return null;
}
default <O> Promise<O> replace(Promise<O> next) {
return null;
}
default <O> Promise<Pair<O, T>> left(Promise<O> left) {
return null;
}
default <O> Promise<Pair<O, T>> left(Function<? super T, ? extends O> leftFunction) {
return null;
}
default <O> Promise<Pair<O, T>> flatLeft(Function<? super T, ? extends Promise<O>> leftFunction) {
return null;
}
default <O> Promise<Pair<T, O>> right(Promise<O> right) {
return null;
}
default <O> Promise<Pair<T, O>> right(Function<? super T, ? extends O> rightFunction) {
return null;
}
default <O> Promise<Pair<T, O>> flatRight(Function<? super T, ? extends Promise<O>> rightFunction) {
return null;
}
default Operation flatOp(Function<? super T, ? extends Operation> function) {
return null;
}
default Promise<T> mapError(Function<? super Throwable, ? extends T> transformer) {
return null;
}
@@ -118,6 +167,10 @@ public interface Promise<T> {
return null;
}
default Promise<T> wiretap(Action<? super Result<T>> listener) {
return null;
}
default Promise<T> fork() {
return null;
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ratpack.exec;
/**
* The result of an asynchronous operation, which may be an error.
*
* @param <T> The type of the successful result object
*/
public interface Result<T> {
static <T> Result<T> success(T value) {
return null;
}
static <T> Result<T> error(Throwable error) {
return null;
}
Throwable getThrowable();
T getValue();
boolean isSuccess();
boolean isError();
default T getValueOrThrow() throws Exception {
return null;
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ratpack.func;
import ratpack.exec.Operation;
/**
* A block of code.
* <p>
* Similar to {@link Runnable}, but allows throwing of checked exceptions.
*/
@FunctionalInterface
public interface Block {
static Block noop() {
return null;
}
void execute() throws Exception;
static Block throwException(final Throwable throwable) {
return null;
}
default Runnable toRunnable() {
return null;
}
default Action<Object> action() {
return null;
}
default <T> T map(Function<? super Block, ? extends T> function) {
return null;
}
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright 2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ratpack.func;
/**
* A generic pair implementation that can be used to cumulatively aggregate a data structure during a promise pipeline.
* <p>
* This can sometimes be useful when collecting facts about something as part of a data stream without using mutable data structures.
* <pre class="java">{@code
* import ratpack.func.Pair;
* import ratpack.exec.Promise;
* import ratpack.test.embed.EmbeddedApp;
*
* import static org.junit.Assert.assertEquals;
*
* public class Example {
*
* public static void main(String[] args) throws Exception {
* EmbeddedApp.fromHandler(ctx -> {
* int id = 1;
* int age = 21;
* String name = "John";
*
* Promise.value(id)
* .map(idValue -> Pair.of(idValue, age))
* .flatMap(pair -> Promise.value(name).map(pair::nestRight))
* .then(pair -> {
* int receivedId = pair.left;
* int receivedAge = pair.right.right;
* String receivedName = pair.right.left;
* ctx.render(receivedName + " [" + receivedId + "] - age: " + receivedAge);
* });
* }).test(httpClient -> {
* assertEquals("John [1] - age: 21", httpClient.getText());
* });
* }
* }
* }</pre>
* <p>
*
* @param <L> the left data type
* @param <R> the right data type
*/
public final class Pair<L, R> {
/**
* The left item of the pair.
*/
public final L left;
/**
* The right item of the pair.
*/
public final R right;
private Pair(L left, R right) {
this.left = null;
this.right = null;
}
public static <L, R> Pair<L, R> of(L left, R right) {
return null;
}
public L getLeft() {
return null;
}
public R getRight() {
return null;
}
public L left() {
return null;
}
public R right() {
return null;
}
public <T> Pair<T, R> left(T newLeft) {
return null;
}
public <T> Pair<L, T> right(T newRight) {
return null;
}
public static <L, R> Pair<L, R> pair(L left, R right) {
return null;
}
public <T> Pair<T, Pair<L, R>> pushLeft(T t) {
return null;
}
public <T> Pair<Pair<L, R>, T> pushRight(T t) {
return null;
}
public <T> Pair<Pair<T, L>, R> nestLeft(T t) {
return null;
}
public <T> Pair<L, Pair<T, R>> nestRight(T t) {
return null;
}
public <T> Pair<T, R> mapLeft(Function<? super L, ? extends T> function) throws Exception {
return null;
}
public <T> Pair<L, T> mapRight(Function<? super R, ? extends T> function) throws Exception {
return null;
}
public <T> T map(Function<? super Pair<L, R>, ? extends T> function) throws Exception {
return null;
}
public static <L, P extends Pair<L, ?>> Function<P, L> unpackLeft() {
return null;
}
public static <R, P extends Pair<?, R>> Function<P, R> unpackRight() {
return null;
}
}