Skip to content

Commit

Permalink
Merge branch '1.3.x'
Browse files Browse the repository at this point in the history
  • Loading branch information
jameskleeh committed Apr 2, 2020
2 parents 8e042a3 + b3daad6 commit e3aa44e
Show file tree
Hide file tree
Showing 18 changed files with 337 additions and 17 deletions.
8 changes: 4 additions & 4 deletions discovery-client/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ dependencies {
exclude module:'micronaut-http-client'
exclude module:'micronaut-inject'
}
compileOnly group: 'com.amazonaws', name: 'aws-java-sdk-route53', version: '1.11.748'
compileOnly group: 'com.amazonaws', name: 'aws-java-sdk-core', version: '1.11.749'
compileOnly group: 'com.amazonaws', name: 'aws-java-sdk-route53', version: '1.11.756'
compileOnly group: 'com.amazonaws', name: 'aws-java-sdk-core', version: '1.11.756'
compileOnly group: 'com.amazonaws', name: 'jmespath-java', version: '1.11.297'
compileOnly group: 'com.amazonaws', name: 'aws-java-sdk-servicediscovery', version: '1.11.688'

Expand All @@ -25,8 +25,8 @@ dependencies {
exclude module:'micronaut-inject'
}

testImplementation group: 'com.amazonaws', name: 'aws-java-sdk-route53', version: '1.11.725'
testImplementation group: 'com.amazonaws', name: 'aws-java-sdk-core', version: '1.11.749'
testImplementation group: 'com.amazonaws', name: 'aws-java-sdk-route53', version: '1.11.756'
testImplementation group: 'com.amazonaws', name: 'aws-java-sdk-core', version: '1.11.755'
testImplementation group: 'com.amazonaws', name: 'jmespath-java', version: '1.11.297'
testImplementation group: 'com.amazonaws', name: 'aws-java-sdk-servicediscovery', version: '1.11.297'
testImplementation group: 'com.amazonaws', name: 'aws-java-sdk-ssm', version: '1.11.308'
Expand Down
4 changes: 2 additions & 2 deletions http-server-netty/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ dependencies {
testImplementation project(":inject")
testImplementation project(":inject-java-test")
testImplementation project(":http-client")
testImplementation group: 'org.powermock', name: 'powermock-module-junit4', version: '2.0.5'
testImplementation group: 'org.powermock', name: 'powermock-api-mockito2', version: '2.0.5'
testImplementation group: 'org.powermock', name: 'powermock-module-junit4', version: '2.0.7'
testImplementation group: 'org.powermock', name: 'powermock-api-mockito2', version: '2.0.7'
testImplementation dependencyModuleVersion("micronaut.test", "micronaut-test-spock"), {
exclude module:'micronaut-runtime'
exclude module:'micronaut-inject'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@

import static io.micronaut.core.util.KotlinUtils.isKotlinCoroutineSuspended;
import static io.micronaut.inject.util.KotlinExecutableMethodUtils.isKotlinFunctionReturnTypeUnit;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;

/**
* Internal implementation of the {@link io.netty.channel.ChannelInboundHandler} for Micronaut.
Expand Down Expand Up @@ -264,7 +266,7 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
}

private void exceptionCaughtInternal(ChannelHandlerContext ctx,
Throwable cause,
Throwable t,
NettyHttpRequest nettyHttpRequest,
boolean nettyException) {
RouteMatch<?> errorRoute = null;
Expand All @@ -275,6 +277,14 @@ private void exceptionCaughtInternal(ChannelHandlerContext ctx,
declaringType = ((MethodExecutionHandle) originalRoute).getDeclaringType();
}

final Throwable cause;
// top level exceptions returned by CompletableFutures. These always wrap the real exception thrown.
if ((t instanceof CompletionException || t instanceof ExecutionException) && t.getCause() != null) {
cause = t.getCause();
} else {
cause = t;
}

// when arguments do not match, then there is UnsatisfiedRouteException, we can handle this with a routed bad request
if (cause instanceof UnsatisfiedRouteException) {
if (declaringType != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2017-2020 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
*
* 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 io.micronaut.http.server.netty.converters;

import java.util.Map;
import java.util.Optional;
import javax.inject.Singleton;

import io.micronaut.core.convert.ConversionContext;
import io.micronaut.core.convert.TypeConverter;
import io.netty.channel.WriteBufferWaterMark;

/**
* Type converter for the newly introduced https://netty.io/4.1/api/io/netty/channel/ChannelOption.html#WRITE_BUFFER_WATER_MARK
* object.
*
* @author ratcashdev
*/
@Singleton
class WriteBufferWaterMarkConverter implements TypeConverter<Map<String, Integer>, WriteBufferWaterMark> {

@Override
public Optional<WriteBufferWaterMark> convert(Map<String, Integer> range,
Class<WriteBufferWaterMark> type, ConversionContext ctx) {
return Optional.of(new WriteBufferWaterMark(range.get("low"), range.get("high")));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.*;
import io.micronaut.http.annotation.Error;
import io.micronaut.http.exceptions.HttpStatusException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

@Requires(property = "spec.name", value = "ExceptionHandlerSpec")
//tag::clazz[]
Expand All @@ -31,6 +34,22 @@ Integer stock(String isbn) {
throw new OutOfStockException();
}

@Produces(MediaType.TEXT_PLAIN)
@Get("/stock/future/{isbn}")
CompletableFuture<Integer> stockFuture(String isbn) {
CompletableFuture future = new CompletableFuture();
future.completeExceptionally(new HttpStatusException(HttpStatus.OK, 1234));
return future;
}

@Produces(MediaType.TEXT_PLAIN)
@Get("/stock/blocking/{isbn}")
Integer stockBlocking(String isbn) throws InterruptedException, ExecutionException {
CompletableFuture<Integer> future = new CompletableFuture<>();
future.completeExceptionally(new HttpStatusException(HttpStatus.OK, 1234));
return future.get();
}

@Produces(MediaType.TEXT_PLAIN)
@Get("/null-pointer")
Integer npe() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,28 @@ class ExceptionHandlerSpec extends Specification {
stock != null
stock == 0
}

void "test wrapped HttpStatusException in CompletionException is unwrapped and handled by ExceptionHandler"() {
when:
HttpRequest request = HttpRequest.GET('/books/stock/future/1234')
Integer stock = client.toBlocking().retrieve(request, Integer)

then:
noExceptionThrown()
stock != null
stock == 1234
}

void "test wrapped HttpStatusException in ExecutionException is unwrapped and handled by ExceptionHandler"() {
when:
HttpRequest request = HttpRequest.GET('/books/stock/blocking/1234')
Integer stock = client.toBlocking().retrieve(request, Integer)

then:
noExceptionThrown()
stock != null
stock == 1234
}

void "test error route with @Status"() {
when:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ class NettyHttpServerConfigurationSpec extends Specification {
'micronaut.server.netty.worker.threads' : 8,
'micronaut.server.netty.parent.threads' : 8,
'micronaut.server.multipart.maxFileSize' : 2048,
'micronaut.server.maxRequestSize' : '2MB']
'micronaut.server.maxRequestSize' : '2MB',
'micronaut.server.netty.childOptions.write_buffer_water_mark.high': 262143,
'micronaut.server.netty.childOptions.write_buffer_water_mark.low' : 65535
]

))
beanContext.start()
Expand All @@ -96,12 +99,15 @@ class NettyHttpServerConfigurationSpec extends Specification {
!config.useNativeTransport
config.maxRequestSize == 2097152
config.multipart.maxFileSize == 2048
config.childOptions.size() == 1
config.childOptions.size() == 2
config.childOptions.keySet().first() instanceof ChannelOption
config.childOptions.keySet()[1] instanceof ChannelOption
config.childOptions.get(ChannelOption.WRITE_BUFFER_WATER_MARK).high == 262143
config.childOptions.get(ChannelOption.WRITE_BUFFER_WATER_MARK).low == 65535
!config.host.isPresent()
config.parent.numOfThreads == 8
config.worker.numOfThreads == 8

then:
NettyHttpServer server = beanContext.getBean(NettyHttpServer)
server.start()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* Copyright 2017-2020 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
*
* 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 io.micronaut.http.server.netty.converters;

import io.micronaut.context.ApplicationContext;
import io.micronaut.context.env.Environment;
import io.micronaut.core.reflect.GenericTypeUtils;
import io.micronaut.core.reflect.ReflectionUtils;
import io.micronaut.http.server.netty.configuration.NettyHttpServerConfiguration;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelOption;
import io.netty.channel.WriteBufferWaterMark;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNotNull;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
*
* @author ratcashdev
*/
public class WriteBufferWaterMarkConverterTest {

NettyHttpServerConfiguration micronautConfig;
Environment environment;
long callCount = 0;
ApplicationContext ctx;

@Before
public void init() {
Map<String, Object> params = new HashMap<>();
params.put("micronaut.server.netty.childOptions.write_buffer_water_mark.high", 262143);
params.put("micronaut.server.netty.childOptions.write_buffer_water_mark.low", 65535);

ctx = ApplicationContext.run(params, (String) null);
micronautConfig = ctx.createBean(NettyHttpServerConfiguration.class);
environment = ctx.getEnvironment();
}

@After
public void tearDown() {
micronautConfig = null;
environment.close();
ctx.close();
}

@Test
public void testNettyConfig() throws IOException {
ServerBootstrap serverBootstrap = new ServerBootstrap();

Map<String, Object> configValue
= (Map<String, Object>) micronautConfig.getChildOptions().get(ChannelOption.WRITE_BUFFER_WATER_MARK);
assertEquals(configValue.get("high"), 262143);
assertEquals(configValue.get("low"), 65535);

processOptions(micronautConfig.getChildOptions(), serverBootstrap::childOption);

WriteBufferWaterMark val = (WriteBufferWaterMark)
serverBootstrap.config().childOptions().get(ChannelOption.WRITE_BUFFER_WATER_MARK);
assertNotNull(val);
assertEquals(val.high(), 262143);
assertEquals(val.low(), 65535);
}

// method copied from NettyHttpServer
private void processOptions(Map<ChannelOption, Object> options, BiConsumer<ChannelOption, Object> biConsumer) {
for (ChannelOption channelOption : options.keySet()) {
String name = channelOption.name();
Object value = options.get(channelOption);
Optional<Field> declaredField = ReflectionUtils.findDeclaredField(ChannelOption.class, name);
declaredField.ifPresent((field) -> {
Optional<Class> typeArg = GenericTypeUtils.resolveGenericTypeArgument(field);
typeArg.ifPresent((arg) -> {
Optional converted = environment.convert(value, arg);
converted.ifPresent((convertedValue) ->
biConsumer.accept(channelOption, convertedValue)
);
});

});
if (!declaredField.isPresent()) {
biConsumer.accept(channelOption, value);
}
}
}
}
Loading

0 comments on commit e3aa44e

Please sign in to comment.