Skip to main content
info

You're seeing the release note of an old version. Check out the latest release note.

v0.97.0

December 8, 2019

🌟 New features

  • We now have a new immutable cookie API with SameSite attribute support, which replaces Netty's cookie API. #1567 #2286

    Cookie c1 = Cookie.of("foo", "bar")
    Cookie c2 = Cookie.builder("alice", "bob")
    .domain("foo.com")
    .path("/bar")
    .maxAge(3600)
    .sameSite("Strict")
    .build();
    // Encoding
    ResponseHeaders resHeaders =
    ResponseHeaders.builder(HttpStatus.OK)
    .add(HttpHeaderNames.SET_COOKIE,
    Cookie.toSetCookieHeaders(c1, c2))
    .build();
    // Decoding
    Cookies cookies =
    Cookie.fromSetCookieHeaders(
    resHeaders.getAll(HttpHeaderNames.SET_COOKIE));
  • You can now replace the request headers of HttpRequest more conveniently using HttpRequest.withHeaders(RequestHeaders). #2278 #2283

    HttpRequest req = HttpRequest.of(HttpMethod.POST, "/post",
    MediaType.PLAIN_TEXT_UTF_8,
    "Hello!");
    RequestHeaders newHeaders = RequestHeaders.of(HttpMethod.PUT, "/put",
    MediaType.PLAIN_TEXT_UTF_8);
    HttpRequest newReq = req.withHeaders(newHeaders);
  • ServiceRequestContext.blockingTaskExecutor() is now a ScheduledExecutorService instead of ExecutorService, which means you can schedule delayed or periodic tasks. #2269

    // A service that sends `PING!` every second.
    HttpService service = (ctx, req) -> {
    HttpResponse res = HttpResponse.streaming();
    res.write(ResponseHeaders.of(200));
    AtomicReference<ScheduledFuture<?>> futureHolder = new AtomicReference<>();
    futureHolder.set(ctx.blockingTaskExecutor().scheduleWithFixedDelay(() -> {
    boolean success = res.tryWrite(HttpData.ofUtf8("PING!\r\n"));
    if (!success) {
    if (futureHolder.get() != null) {
    futureHolder.get().cancel(false);
    }
    }
    }, 1, TimeUnit.SECONDS));
    return res;
    };
  • You can now add converters and exception handlers to annotated services more conveniently using the new fluent builder methods. #2242

    Server server =
    Server.builder()
    .annotatedService().requestConverters(converterA, converterB)
    .responseConverters(converterC, converterD)
    .exceptionHandlers(handlerA, handlerB)
    .build(myAnnotatedService)
    .build();
  • You can now configure how Armeria decides ServiceRequestContext.clientAddress() via ServerBuilder.clientAddressMapper(). #1631 #2294

    Server.builder()
    .clientAddressMapper(proxiedAddrs -> {
    InetSocketAddress srcAddr = proxiedAddrs.sourceAddress();
    List<InetSocketAddress> destAddrs = proxiedAddrs.destinationAddresses();
    if (destAddrs.isEmpty()) {
    // No proxy servers involved.
    return srcAddr;
    } else {
    // When there are more than one proxy server involved,
    // trust only the address given by the last proxy server.
    return destAddrs.get(destAddrs.size() - 1);
    }
    });
  • You can now choose a different SLF4J Logger than the default one when using LoggingClient or LoggingService. #2220 #2237

    Logger myLogger = LoggerFactory.getLogger(MyService.class);
    Server server =
    Server.builder()
    .service("/",
    myService.decorate(LoggingService.builder()
    .logger(myLogger)
    .newDecorator()))
    .build();
  • BraveClient now adds connection timing information to a span. #2271 #2273

    • connection-acquire.start and connection-acquire.end
    • dns-resolve.start and dns-resolve.end
    • socket-connect.start and socket-connect.end
    • connection-reuse.start and connection-reuse.end
  • withDeadlineAfter is now supported for gRPC client stubs. #2284

  • GrpcService now allows controlling whether to respect the "grpc-timeout" header sent by a gRPC client. #2284

    • Consider disabling this feature via GrpcServiceBuilder.useClientTimeoutHeader() in an insecure environment.
  • THttpService and ThriftCallService now supports adding more than one Thrift service at the same endpoint. #2164 #2285

    class MyFooService implements FooService.Iface ... {
    public void foo() { ... }
    }

    class MyBarService implements BarService.Iface ... {
    public void bar() { ... }
    }

    // "foo" call goes to MyFooService and
    // "bar" call goes to MyBarService.
    Server server =
    Server.builder()
    .service("/thrift",
    THttpService.builder()
    .addService(new MyFooService())
    .addService(new MyBarService())
    .build())
    .build();
  • armeria-spring-boot-actuator-autoconfigure now supports the official CORS settings. #2063

    # application.yml
    management.endpoints.web.cors.allowed-origins: https://example.com
    management.endpoints.web.cors.allowed-methods: GET,POST

📈 Improvements

  • Improved routing performance. #2293 #2295
  • The core JAR file is now smaller, by removing unnecessary resources such as web fonts and favicons. #2299 #2300
  • HttpRequest.of(RequestHeaders, Publisher) now handles the case where the Publisher is actually an HttpRequest. #2278 #2283

🔒 Security fixes

🛠️ Bug fixes

  • The event loop threads are properly cleaned up when Server failed to start. #2288
  • Server now sends a redirect response correctly for the requests without a trailing slash (/) even when you bound a fallback service at prefix:/. #2116 #2292
  • Fixed a bug where routing cache did not work as expected, leading suboptimal routing performance. #2293
  • Annotated services now accept only the first "Cookie" header when injecting Cookies, because multiple "Cookie" headers must be prohibited according to RFC 6265. #2286
  • GrpcService now respects gRPC stub deadline. #2008 #2284

🏚️ Deprecations

  • Deprecated HttpRequest.of(HttpRequest, RequestHeaders) in favor of HttpRequest.withHeaders() #2283
  • Deprecated HttpRequest.of(AggregatedHttpRequest) and HttpResponse.of(AggregatedHttpResponse) in favor of AggregatedHttpRequest.toHttpRequest() and AggregatedHttpResponse.toHttpResponse(). #2283
    • These methods were removed from AggregatedHttpMessage some time ago due to ambiguity, but now we are reviving them because we have separate types for requests and responses.
  • Cookies.copyOf() has been deprecated in favor of Cookies.of(). #2286
  • AnnotatedServiceBuildingBuilder.requestConverter(), responseConverter() and exceptionHandler() have been deprecated in favor of requestConverters(), responseConverters() and exceptionHandlers() respectively. #2242
  • Sampler.rateLimited() has been deprecated in favor of rateLimiting(). #2122 #2289
  • THttpService.of() and ofFormats() factory methods that require a Map<String, ?> have been deprecated. Use THttpService.builder() and call THttpService.addService(String, Object) multiple times instead. #2285

☢️ Breaking changes

  • ServerBuilder.blockingTaskExecutor() now requires a ScheduledExecutorService instead of an ExecutorService. #2269
  • The behavior of automatic redirection for the case of missing trailing slash (/) has been changed (in a good way). #2116 #2292
    • For example, previously, when a client sent a request to /docs, the following server routed the request to TomcatService:
      Server server =
      Server.builder()
      .serviceUnder("/docs", new DocService())
      .serviceUnder("/", TomcatSerivce.forClassPath(...))
      .build();
    • However, from this release, the request will not be routed to TomcatService but the server will issue a redirect response to /docs/.
  • ProxiedAddresses class has been redesigned to handle the case of more than one intermediary proxy server. #1631 #2294
  • Cookies now uses Armeria's own Cookie. #2286
  • Cookies now may contain the cookies with the same name. #2286
    • Note that RFC does not prohibit duplicate names at all, so this is correct behavior.
  • Annotated services now accept only the first "Cookie" header when decoding cookies. Sending multiple "Cookie" headers was a violation of RFC 6265 anyway. #2286
  • The ThriftCallService factory method with signature of(Map<String, ? extends Iterable<?>>) has been changed to of(Map<String, ? extends Iterable<?>>) #2285

⛓ Dependencies

  • Brave 1.3.1
  • Jetty 9.4.22 -> 9.4.24
  • Micrometer 1.3.1 -> 1.3.2
  • Netty TCNative BoringSSL 2.0.27 -> 2.0.26
  • Project Reactor 3.3.0 -> 3.3.1
  • RxJava2 2.2.14 -> 2.2.15
  • Spring Boot 2.1.10 -> 2.2.1
  • Tomcat 9.0.27 -> 9.0.29, 8.5.47 -> 8.5.49

🙇 Thank you

This release was possible thanks to the following contributors who shared their brilliant ideas and awesome pull requests:

@renaudb@monorisk@minwoox@tobias-@heowc@chrsow@imasahiro@ikhoon@anuraaga@JunoJunho@magnuschatt@stypr@hyangtack@trustin@JLLeitschuh@sivaalli

Like Armeria?
Star us ⭐️

×