0.94.0 release notes

4th October 2019

New features

  • You can now decorate multiple services by path mapping. See the documentation for more information. #582 #2040 #2057

    ServerBuilder sb = new ServerBuilder();
    // Register vipService and memberService under '/users' path
    sb.annotatedService("/users/vip", vipService)
      .annotatedService("/users/members", memberService);
    // Decorate all services under '/users' path
    sb.decoratorUnder("/users", (delegate, ctx, req) -> {
        if (!authenticate(req)) {
            return HttpResponse.of(HttpStatus.UNAUTHORIZED);
        return delegate.serve(ctx, req);

    You can also use fluent route builder with routeDecorator() to decorate more than one service by path mapping.

    // Decorate services under '/users' path with fluent route builder
      .build((delegate, ctx, req) -> {
          if (!authenticate(req)) {
              return HttpResponse.of(HttpStatus.UNAUTHORIZED);
          return delegate.serve(ctx, req);
  • You can now get the current HttpRequest and RpcRequest from RequestContext so you don’t need to downcast Request to HttpRequest or RpcRequest. #2089 #2120

    // Before:
    Request req = ctx.request();
    if (req instanceof HttpRequest) {
        RequestHeaders headers = (HttpRequest) req).headers();
    // After:
    RequestHeaders headers = ctx.request().headers();
    // Before:
    if (req instanceof RpcRequest) {
        String rpcMethod = (RpcRequest) requestContent).method();
    // After:
    // `rpcRequest()` method will return `null` when the request being handled is not
    // an RPC request or not decoded into an RPC request yet.
    String rpcMethod = ctx.rpcRequest().method();
  • You can now set example headers when using {Annotated,Grpc,Thrift}ServiceRegisrationBean for Spring Boot integration. #2100

    public AnnotatedServiceRegistrationBean annotatedService() {
        return new AnnotatedServiceRegistrationBean()
                   .setService(new AnnotatedService())
                   // Add example headers for annotated service
                   .addExampleHeaders("x-additional-header", "headerVal")
                   .addExampleHeaders("get", "x-additional-header", "headerVal");
  • You can now create the following classes using the builder() method instead of their *Builder constructors. #1719 #2085

    • CircuitBreaker
    • CircuitBreakerHttpClient
    • CircuitBreakerRpcClient
    • DnsAddressEndpointGroup
    • DnsServiceEndpointGroup
    • DnsTextEndpointGroup
    • GrpcService
    • Server
    • RetryingHttpClient
    • RetryingRpcClient
      // Before:
      Server server = new ServerBuilder()
                            .service("/hello", (ctx, req) -> HttpResponse.of(OK))
      // After:
      Server server = Server.builder()
                            .service("/hello", (ctx, req) -> HttpResponse.of(OK))


Bug fixes

  • ResponseTimeoutException is not logged more than once anymore when the response has been timed out. #2000 #2138
  • You no longer see AbortedStreamException while sending long-lived requests with RetryingHttpClient. #2134
  • You can now see a warning message when JSON request conversion fails in an annotated service. #2041 #2131


  • AbstractBindingBuilder.pathUnder(String prefix) has been deprecated in favor of pathPrefix(String prefix). #2040
  • RouteBuilder.prefix(String prefix, ...) has been deprecated in favor of pathPrefix(String prefix, ...). #2040
  • RouteBuilder.pathWithPrefix(String prefix, String pathPattern) has been deprecated in favor of path(String prefix, String pathPattern). #2040
  • new *Builder() constructors which are mentioned in 'New Features' have been deprecated in favor of *.builder(). #1719 #2085

Breaking changes

  • armeria-zipkin has been removed for further clean-up. #2120
  • RequestContext.request() returns HttpRequest instead of Request. #2120
    • RequestContext.updateRequest() always updates an HttpRequest. It returns void now because it never fails, unless null is specified.
    • RequestContext.newDerivedContext(Request) now requires both HttpRequest and RpcRequest.
  • A default virtual host service can serve any virtual host requests. #2057 #2040
    • Before: If a custom virtual host fails to match the given request to a service, it returns NOT_FOUND status.
    • After: If a custom virtual host fails to match the given request to a service, looking up a default virtual host services to match the request.
  • AbstractStreamMessageDuplicator.close() does not abort all children StreamMessages. #2134
    • You should use AbstractStreamMessageDuplicator.abort() to abort all children StreamMessages anymore.


  • gRPC 1.23.0 -> 1.24.0
  • Dropwizard Metrics 4.0.0 -> 4.1.0
  • Jetty 9.4.20.v20190813 -> 9.4.21.v20190926
  • Jackson -> 2.10.0
  • Java JWT 3.8.2 -> 3.8.3
  • Micrometer 1.2.1 -> 1.3.0

Thank you