Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion fuzzing-tests/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ group = "io.micronaut.fuzzing"

dependencies {
implementation(mn.micronaut.http.server.netty)
implementation(mn.micronaut.http.client.core)
implementation(mn.micronaut.http.client)
implementation(mn.micronaut.jackson.databind)
implementation(mn.reactor)

Expand All @@ -28,7 +30,6 @@ dependencies {

testImplementation(mnTest.junit.jupiter.engine)
testImplementation(mnTest.junit.jupiter.params)
testImplementation(mn.micronaut.http.client)
testImplementation(mnTest.micronaut.test.junit5)
testAnnotationProcessor(mn.micronaut.inject.java)
}
Expand Down
77 changes: 77 additions & 0 deletions fuzzing-tests/src/main/java/io/micronaut/fuzzing/http/Api.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2017-2024 original 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
*
* https://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 io.micronaut.fuzzing.http;

import io.micronaut.core.async.annotation.SingleResult;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Consumes;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import jakarta.inject.Singleton;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.concurrent.CompletionStage;

@Singleton
@Controller
public interface Api {
String ECHO_PUBLISHER = "/echo-publisher";
String ECHO_MONO = "/echo-mono";
String ECHO_FLUX = "/echo-flux";
String ECHO_FUTURE = "/echo-future";
String ECHO_STRING_PUBLISHER = "/echo-string-publisher";
String ECHO_ARRAY = "/echo-array";
String ECHO_STRING = "/echo-string";
String ECHO_PIECE_JSON = "/echo-piece-json";

@Get
String index();

@Post(ECHO_PUBLISHER)
Publisher<byte[]> echo(@Body Publisher<byte[]> foo);

@SingleResult
@Post(ECHO_STRING_PUBLISHER)
Publisher<String> echoString(@Body Publisher<String> foo);

@Post(ECHO_MONO)
Mono<String> echoMono(@Body Mono<String> foo);

@SingleResult
@Post(ECHO_FLUX)
Flux<String> echoFlux(@Body Flux<String> foo);

@Post(ECHO_FUTURE)
CompletionStage<String> echoFuture(@Body CompletionStage<String> foo);

@Post(ECHO_ARRAY)
byte[] echo(@Body byte[] foo);

@Post(ECHO_STRING)
String echo(@Body String foo);

@Post(ECHO_PIECE_JSON)
@Consumes({
MediaType.APPLICATION_JSON,
MediaType.APPLICATION_FORM_URLENCODED
})
String echoPieceJson(@Body("foo") String foo);

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@
SimpleController.ECHO_PUBLISHER,
SimpleController.ECHO_STRING,
SimpleController.ECHO_PIECE_JSON,

SimpleController.ECHO_STRING_PUBLISHER,
SimpleController.ECHO_MONO,
SimpleController.ECHO_FLUX,
SimpleController.ECHO_FUTURE,
RedirectingController.REDIRECT_PUBLISHER,
RedirectingController.REDIRECT_STRING_PUBLISHER,
RedirectingController.REDIRECT_MONO,
RedirectingController.REDIRECT_FLUX,
RedirectingController.REDIRECT_FUTURE,
RedirectingController.REDIRECT_ARRAY,
RedirectingController.REDIRECT_STRING,

})
public class EmbeddedHttpTarget implements AutoCloseable {
static final String SEPARATOR = "SEP";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package io.micronaut.fuzzing.http;

import io.micronaut.core.async.annotation.SingleResult;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Post;
import io.micronaut.http.client.annotation.Client;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.concurrent.CompletionStage;

@Client("/")
public interface HttpClientApi extends Api {

@Post(ECHO_PUBLISHER)
Publisher<byte[]> echoPublisher(@Body byte[] foo);

@SingleResult
@Post(ECHO_STRING_PUBLISHER)
Publisher<String> echoString(@Body String foo);

@Post(ECHO_MONO)
Mono<String> echoMono(@Body String foo);

// Casting problem
@Post(ECHO_MONO)
Publisher<String> echoMonoPublisher(@Body String foo);

@SingleResult
@Post(ECHO_FLUX)
Flux<String> echoFlux(@Body String foo);

@Post(ECHO_FUTURE)
CompletionStage<String> echoFuture(@Body String foo);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright 2017-2024 original 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
*
* https://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 io.micronaut.fuzzing.http;

import io.micronaut.core.async.annotation.SingleResult;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Consumes;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import io.micronaut.scheduling.TaskExecutors;
import io.micronaut.scheduling.annotation.ExecuteOn;
import jakarta.inject.Singleton;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

@Singleton
@Controller(RedirectingController.REDIRECT)
public final class RedirectingController implements Api {
static final String REDIRECT = "/redirect";
static final String REDIRECT_PUBLISHER = REDIRECT + Api.ECHO_PUBLISHER;
static final String REDIRECT_MONO = REDIRECT + Api.ECHO_MONO;
static final String REDIRECT_FLUX = REDIRECT + Api.ECHO_FLUX;
static final String REDIRECT_FUTURE = REDIRECT + Api.ECHO_FUTURE;
static final String REDIRECT_STRING_PUBLISHER = REDIRECT + Api.ECHO_STRING_PUBLISHER;
static final String REDIRECT_ARRAY = REDIRECT + Api.ECHO_ARRAY;
static final String REDIRECT_STRING = REDIRECT + Api.ECHO_STRING;
static final String REDIRECT_PIECE_JSON = REDIRECT + Api.ECHO_PIECE_JSON;

private final HttpClientApi clientApi;

public RedirectingController(HttpClientApi clientApi) {
this.clientApi = clientApi;
}

public String index() {
return clientApi.index();
}

public Publisher<byte[]> echo(@Body Publisher<byte[]> foo) {
return Mono.from(foo).flatMap(body -> Mono.from(clientApi.echoPublisher(body)));
}

public Publisher<String> echoString(@Body Publisher<String> foo) {
return Mono.from(foo).flatMap(body -> Mono.from(clientApi.echoString(body)));
}

public Mono<String> echoMono(@Body Mono<String> foo) {
return foo.flatMap(body -> Mono.from(clientApi.echoMonoPublisher(body)));
}

public Flux<String> echoFlux(@Body Flux<String> foo) {
return foo.flatMap(clientApi::echoFlux);
}

public CompletionStage<String> echoFuture(@Body CompletionStage<String> foo) {
return foo.thenCompose(clientApi::echoFuture);
}

@ExecuteOn(TaskExecutors.BLOCKING)
public byte[] echo(@Body byte[] foo) {
return clientApi.echo(foo);
}

@ExecuteOn(TaskExecutors.BLOCKING)
public String echo(@Body String foo) {
return clientApi.echo(foo);
}

@ExecuteOn(TaskExecutors.BLOCKING)
public String echoPieceJson(@Body("foo") String foo) {
return clientApi.echoPieceJson(foo);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,48 +15,60 @@
*/
package io.micronaut.fuzzing.http;

import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Body;
import io.micronaut.http.annotation.Consumes;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import io.micronaut.http.annotation.Post;
import jakarta.inject.Singleton;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.concurrent.CompletionStage;

@Singleton
@Controller
public final class SimpleController {
static final String ECHO_PUBLISHER = "/echo-publisher";
static final String ECHO_ARRAY = "/echo-array";
static final String ECHO_STRING = "/echo-string";
static final String ECHO_PIECE_JSON = "/echo-piece-json";
public final class SimpleController implements Api {

@Get
@Override
public String index() {
return "index";
}

@Post(ECHO_PUBLISHER)
@Override
public Publisher<byte[]> echo(@Body Publisher<byte[]> foo) {
return foo;
}

@Post(ECHO_ARRAY)
@Override
public Publisher<String> echoString(@Body Publisher<String> foo) {
return foo;
}

@Override
public Mono<String> echoMono(@Body Mono<String> foo) {
return foo;
}

@Override
public Flux<String> echoFlux(@Body Flux<String> foo) {
return foo;
}

@Override
public CompletionStage<String> echoFuture(@Body CompletionStage<String> foo) {
return foo;
}

@Override
public byte[] echo(@Body byte[] foo) {
return foo;
}

@Post(ECHO_STRING)
@Override
public String echo(@Body String foo) {
return foo;
}

@Post(ECHO_PIECE_JSON)
@Consumes({
MediaType.APPLICATION_JSON,
MediaType.APPLICATION_FORM_URLENCODED
})
@Override
public String echoPieceJson(@Body("foo") String foo) {
return foo;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import io.micronaut.test.extensions.junit5.annotation.MicronautTest;
import jakarta.inject.Inject;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
Expand All @@ -25,9 +26,21 @@ void simple() {
@ParameterizedTest
@ValueSource(strings = {
SimpleController.ECHO_PUBLISHER,
SimpleController.ECHO_STRING_PUBLISHER,
SimpleController.ECHO_MONO,
SimpleController.ECHO_FLUX,
SimpleController.ECHO_FUTURE,
SimpleController.ECHO_ARRAY,
SimpleController.ECHO_STRING
SimpleController.ECHO_STRING,
RedirectingController.REDIRECT_PUBLISHER,
RedirectingController.REDIRECT_STRING_PUBLISHER,
RedirectingController.REDIRECT_MONO,
RedirectingController.REDIRECT_FLUX,
RedirectingController.REDIRECT_FUTURE,
RedirectingController.REDIRECT_ARRAY,
RedirectingController.REDIRECT_STRING,
})

void echo(String path) {
Assertions.assertEquals("{}", client.toBlocking().retrieve(HttpRequest.POST(path, "{}")));
}
Expand All @@ -37,4 +50,11 @@ void echoPiece() {
Assertions.assertEquals("bar", client.toBlocking().retrieve(HttpRequest.POST(SimpleController.ECHO_PIECE_JSON, "{\"foo\": \"bar\"}")));
Assertions.assertEquals("bar", client.toBlocking().retrieve(HttpRequest.POST(SimpleController.ECHO_PIECE_JSON, "foo=bar").contentType(MediaType.APPLICATION_FORM_URLENCODED)));
}

@Disabled // TODO: 4.8 DefinitionType SERVER to reverse media types for the API interface,
@Test
void redirectPiece() {
Assertions.assertEquals("bar", client.toBlocking().retrieve(HttpRequest.POST(RedirectingController.REDIRECT_PIECE_JSON, "{\"foo\": \"bar\"}")));
Assertions.assertEquals("bar", client.toBlocking().retrieve(HttpRequest.POST(RedirectingController.REDIRECT_PIECE_JSON, "foo=bar").contentType(MediaType.APPLICATION_FORM_URLENCODED)));
}
}