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
@Nullable
annotation to specify an optional parameter or request object in annotated services. Previously, only the parameters with@Default
annotation orOptional
type 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.time
package 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
CircuitBreakerClient
andRetryingClient
usingonResponseTrailers()
method. This can be useful when you work with gRPC, whose status code is encoded in thegrpc-status
trailer. #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);
RequestLog
sanitizers now acceptRequestContext
as 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
OAuth1aToken
more 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.port
property 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
PooledHttpData
in a relatively safer way. #2448- See
PooledHttpData
,PooledWebClient
,PooledHttpRequest
andPooledHttpResponse
for 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
WebClient
was 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
,1
and0
are accepted. Other values will cause a400 Bad Request
response.
- Only
DocService
web UI now shows the 'request body' field forDELETE
andPATCH
methods in the debug form. #2756 #2819JacksonRequestConverterFunction
now handles the case where the target type has a type parameter, e.g.List<Long>
. #2769 #2779- Fixed a bug where the current
ServiceRequestContext
is not pushed when invokingResponseConverterFunction.convertResponse()
. #2789 RequestContextExportingAppender
now handles the<exports />
tag correctly. #2781- Fixed a bug where some Reactive Streams
Subscriber
implementations violate the specification. #2815 - You no longer get sporadic
WriteTimeoutException
from proxied connections. #2801 #2805 - You no longer get a
CancelledSubscriptionException
unnecessarily when usingPublisherBasedStreamMessage
. #2797 - You no longer get sporadic
EncoderException
s from HTTP/1 connections. #2765 - It's now disallowed to specify the following headers in gRPC
Metadata
. #2718:status
grpc-message
grpc-status
armeria.grpc.ThrowableProto-bin
🏚️ Deprecations
AbstractUnwrappable.delegate()
has been deprecated in favor ofUnwrappable.unwrap()
.
☢️ Breaking changes
- You can specify an absolute URL only when a
WebClient
was 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
MeterIdPrefixFunction
returned byMeterIdPrefixFunction.ofDefault()
now generates the meter IDs with different tags. #2780- The
route
tag has been replaced with theservice
tag.
- The
RetrofitMeterIdPrefixFunctionBuilder
has been removed and superseded byRequestOnlyLog.serviceName()
. #2780- ZooKeeper-based service discovery: #2749 #2791
- You now must specify
ZooKeeperRegistrationSpec
orZooKeeperDiscoverySpec
when creatingZooKeeperUpdatingListener
orZooKeeperEndpointGroup
. NodeValueCodec
has been removed.ZooKeeperEndpointGroupBuilder.codec()
andZooKeeperUpdatingListenerBuilder.codec()
have been removed.
- You now must specify
ByteBufHttpData
has been replaced withPooledHttpData
. #2448HttpRequest.aggregateWithPooledObjects()
andHttpResponse.aggregateWithPooledObject
have been replaced withPooledHttpRequest
andPooledHttpResponse
. #2448SubscriptionOption.WITH_POOLED_OBJECTS
has 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