0.99.7 release notes
22nd June 2020
🌟 New features
You can now specify the maximum lifespan of server-side connections using
ServerBuilder.maxConnectionAge(). This is useful when you have to deal with a load balancer without HTTP/2 support. #2747 #2796Server server = Server.builder() .maxConnectionAge(Duration.ofMinutes(1)) ... .build();You can now record the name of the service that handled a request into
RequestOnlyLog.serviceName(). #2768 #2780 #2809 #2820By using
ServiceBindingBuilder.defaultServiceName():Server server = Server.builder() .route().path("/service") .defaultServiceName("my-service") .build(new MyService()) .build();By using
@ServiceName:@ServiceName("my-service") public class MyAnnotatedService { @Get("/get") public String get() { ... } @Post("/set") @ServiceName("my-post-service") public String post(@Param String value) { ... } }Programmatically:
Server server = Server.builder() .service("/service", (ctx, req) -> { ctx.logBuilder().serviceName("my-service"); }) .build();Armeria will use the FQCN of the service class if you did not specify a service name.
You can now use
@Nullableannotation to specify an optional parameter or request object in annotated services. Previously, only the parameters with@Defaultannotation orOptionaltype were accepted. #2773public class MyAnnotatedService { // null will be injected into 'value' instead of returning // '400 Bad Request' when 'value' is missing. @Get("/get") public String get(@Param @Nullable String value) { ... } }You can now use the classes in the
java.timepackage in annotated services. #2760 #2783 #2792 #2799public class MyAnnotatedService { @Get("/sleep/{duration}") public void sleep(@Param Duration duration) { ... } }You can now determine whether a request was successful or not from HTTP trailers in
CircuitBreakerClientandRetryingClientusingonResponseTrailers()method. This can be useful when you work with gRPC, whose status code is encoded in thegrpc-statustrailer. #2816CircuitBreaker cb = CircuitBreaker.of("my-service"); CircuitBreakerRule cbr = CircuitBreakerRule.builder() .onResponseTrailers(trailers -> { return trailers.getInt("grpc-status", -1) != 0; }) .thenFailure() .build(); MyServiceStub myService = Clients.builder("gproto+h2c://example.com/") .decorator(CircuitBreakerClient.newDecorator(cb, cbr)) .build(MyServiceStub.class);RequestLogsanitizers now acceptRequestContextas an additional input, so that the sanitizers can behave differently depending on the current path, etc. #2803Server server = Server.builder() .decorator(LoggingService.builder() .headersSanitizer((ctx, headers) -> { if (ctx.path().startsWith("/secret/")) { return "<secret>"; } else { return headers; } }) .newDecorator()) ... .build();Armeria now supports service discovery and registration for Curator Service Discovery and Finagle ServerSets. #2673 #2749 #2791
CuratorFramework curator = ...; // Client side: //// Curator Service Discovery: EndpointGroup curatorEndpointGroup = ZooKeeperEndpointGroup.of(curator, "/discovery/curator", ZooKeeperDiscoverySpec.curator("my-service")); //// Finagle ServerSets: EndpointGroup serverSetsEndpointGroup = ZooKeeperEndpointGroup.of(curator, "/discovery/serversets" ZooKeeperDiscoverySpec.serverSets()); // Server-side: Server server = ...; //// Curator Service Discovery: server.addListener(ZooKeeperUpdatingListener.of( curator, "/discovery/curator", ZooKeeperRegistrationSpec.curator("my-service"))); //// Finagle ServerSets: server.addListener(ZooKeeperUpdatingListener.of( curator, "/discovery/serversets", ZooKeeperRegistrationSpec.serverSets()));You can now build
OAuth1aTokenmore conveniently using the builder API. #2770OAuth1aToken token = OAuth1aToken.builder() .realm("...") .consumerKey("...") .token("...") ... .build();The Spring Boot integration now hides all services under
/internal/for non-management ports whenmanagement.server.portproperty is set. #2408 #2502The Spring Boot integration now supports the new graceful shutdown properties introduced in Spring Boot 2.3. #2784 #2802
Added a new API for handling reference counted or pooled objects such as
PooledHttpDatain a relatively safer way. #2448- See
PooledHttpData,PooledWebClient,PooledHttpRequestandPooledHttpResponsefor more information.
- See
📈 Improvements
- Cleaned up minor issues reported by errorprone. #2772
- Made some exception messages more user-friendly. #2751
🛠️ Bug fixes
- It's now allowed to specify an absolute URL only when a
WebClientwas created without a base URL. #2757WebClient clientWithoutBaseUrl = WebClient.of(); WebClient clientWithBaseUrl = WebClient.of("https://example.com/"); // Good clientWithBaseUrl.get("/bar"); clientWithoutBaseUrl.get("https://foo.com/"); // Bad clientWithBaseUrl.get("https://foo.com/"); clientWithoutBaseUrl.get("/bar"); - Boolean parameter conversion became more strict in annotated services. #2767 #2774
- Only
true,false,1and0are accepted. Other values will cause a400 Bad Requestresponse.
- Only
DocServiceweb UI now shows the 'request body' field forDELETEandPATCHmethods in the debug form. #2756 #2819JacksonRequestConverterFunctionnow handles the case where the target type has a type parameter, e.g.List<Long>. #2769 #2779- Fixed a bug where the current
ServiceRequestContextis not pushed when invokingResponseConverterFunction.convertResponse(). #2789 RequestContextExportingAppendernow handles the<exports />tag correctly. #2781- Fixed a bug where some Reactive Streams
Subscriberimplementations violate the specification. #2815 - You no longer get sporadic
WriteTimeoutExceptionfrom proxied connections. #2801 #2805 - You no longer get a
CancelledSubscriptionExceptionunnecessarily when usingPublisherBasedStreamMessage. #2797 - You no longer get sporadic
EncoderExceptions from HTTP/1 connections. #2765 - It's now disallowed to specify the following headers in gRPC
Metadata. #2718:statusgrpc-messagegrpc-statusarmeria.grpc.ThrowableProto-bin
🏚️ Deprecations
AbstractUnwrappable.delegate()has been deprecated in favor ofUnwrappable.unwrap().
☢️ Breaking changes
- You can specify an absolute URL only when a
WebClientwas created without a base URL. It was previously allowed to specify an absolute URL even when created with a base URL. #2757 - The method signature of
RequestConverterFunction.convertRequest()has been changed to support parameterized types. #2779 - The default
MeterIdPrefixFunctionreturned byMeterIdPrefixFunction.ofDefault()now generates the meter IDs with different tags. #2780- The
routetag has been replaced with theservicetag.
- The
RetrofitMeterIdPrefixFunctionBuilderhas been removed and superseded byRequestOnlyLog.serviceName(). #2780- ZooKeeper-based service discovery: #2749 #2791
- You now must specify
ZooKeeperRegistrationSpecorZooKeeperDiscoverySpecwhen creatingZooKeeperUpdatingListenerorZooKeeperEndpointGroup. NodeValueCodechas been removed.ZooKeeperEndpointGroupBuilder.codec()andZooKeeperUpdatingListenerBuilder.codec()have been removed.
- You now must specify
ByteBufHttpDatahas been replaced withPooledHttpData. #2448HttpRequest.aggregateWithPooledObjects()andHttpResponse.aggregateWithPooledObjecthave been replaced withPooledHttpRequestandPooledHttpResponse. #2448SubscriptionOption.WITH_POOLED_OBJECTShas been removed. #2448
⛓ Dependencies
- Bouncy Castle 1.65 → 1.65.01
- Dropwizard 2.0.9 → 2.0.10
- gRPC 1.29.0 → 1.30.1
- Jetty 9.4.29 → 9.4.30
- Reactor 3.3.5 → 3.3.6
- Spring Boot 2.3.0 → 2.3.1, 2.1.14 → 2.1.15
- Tomcat 9.0.35 → 9.0.36, 8.5.55 → 8.5.56
- Example dependencies
- Dagger 2.27 → 2.28
- grpc-kotlin-stub 0.1.2 → 0.1.3