1.19.0 release notes

13th September 2022

🌟 New features

  • You can now repeatedly aggregate an HttpRequest and HttpResponse when using HttpRequest.aggregate() and HttpResponse.aggregate(). If an HttpRequest or an HttpResponse is aggregated before, the cached AggregatedHttpRequest or AggregatedHttpResponse is returned for subsequent calls. #3789 #4366

    HttpRequest request = ...;
    AggregatedHttpRequest aggregatedReq0 = request.aggregate().join();
    AggregatedHttpRequest aggregatedReq1 = request.aggregate().join();
    assert aggregatedReq0 == aggregatedReq1;
    
    HttpResponse response = ...;
    AggregatedHttpResponse aggregatedRes0 = response.aggregate().join();
    AggregatedHttpResponse aggregatedRes1 = response.aggregate().join();
    assert aggregatedRes0 == aggregatedRes1;
  • You can now customize the behavior of auto-refresh for an AddressResolverGroup with DnsResolverGroupBuilder. #4400

    // Disable auto refresh
    ClientFactory
      .builder()
      .domainNameResolverCustomizer(customizer -> {
        customizer.enableAutoRefresh(false); // 👈👈👈
      })
      .build();
    
    // Set a constant refresh timeout
    ClientFactory
      .builder()
      .domainNameResolverCustomizer(customizer -> {
        customizer.autoRefreshTimeout(Duration.ofDays(1)); // 👈👈👈
      })
      .build();
    
    // Build a flexible refresh timeout depending on the hostname
    ClientFactory
      .builder()
      .domainNameResolverCustomizer(customizer -> {
        customizer.autoRefreshTimeout(hostname -> {  // 👈👈👈
          if (hostname.endsWith("busy.domain.com")) {
            // Automatically refresh the cached domain for 7 days.
            return Duration.ofDays(7).toMillis();
          }
          if (hostname.endsWith("sporadic.domain.dom")) {
            // Don't need to refresh a sporadically used domain.
            return 0;
          }
          ...
        });
      })
      .build();
  • You can now export RequestOnlyLog.requestCause() and RequestLog.responseCause() to MDC by specifying req.cause and res.cause to an appender of a Logback configuration.

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
      <appender name="RCEA" class="com.linecorp.armeria.common.logback.RequestContextExportingAppender">
        ...
        <export>req.cause</export>  <!-- 👈👈👈 -->
        <export>res.cause</export>  <!-- 👈👈👈 -->
        ...
      </appender>
    </configuration>
  • You can now trace a RequestContext leak by specifying -Dcom.linecorp.armeria.requestContextLeakDetectionSampler=<sampler-spec> or implementing a custom FlagsProvider. #4100 #4232

    public final class LeakDetectingFlagsProvider implements FlagsProvider {
      @Override
      public Sampler<? super RequestContext> requestContextLeakDetectionSampler() {
        // Samples all request contexts.
        return Sampler.always();
      }
    }
  • DocService now automatically generates a specification of annotated services for well known types. #4309 #4322

    • Java POJO
    • Kotlin data class
    • Scala case class
    • Protocol Buffers' Message
    • ScalaPB's GeneratedMessage
  • You can now upload files to your GraphqlService using GraphQL multipart requests. #3971 #4199

  • You can now use armeria-version-catalog module to easily import Armeria and Armeria's direct dependencies using Gradle's version catalog. #4377

    // in settings.gradle
    dependencyResolutionManagement {
      versionCatalogs {
        create("armeriaDeps") {
          from("com.linecorp.armeria:armeria-version-catalog:1.19.0")
        }
      }
    }

📈 Improvements

  • CompositeException now shows full stack traces of all nested exceptions when -Dcom.linecorp.armeria.verboseExceptions=true is set. #4347 #4356

🛠️ Bug fixes

  • You can now use Armeria Spring actuator with WebFlux correctly. #4396 #4408

  • You no longer see ClassCastException when an invalid path request is received. #4405

  • Annotated services now correctly return 204 No Content status when void, Publisher<Void> or CompletionStage<Void> and @ProducesJson are used at the same time. #4390 #4398

    // 200 OK status and "null" string body is returned
    @Get("/null-content")
    @ProducesJson
    public Publisher<Void> nullBody() {
      return Mono.empty();
    }
    
    // 204 No Content status and no body is returned
    @Get("/no-content")
    @StatusCode(204)
    @ProducesJson
    public Publisher<Void> noBody() {
      return Mono.empty();
    }
  • Armeria server no longer sends chunked encoding header with 304 Not Modified status. #4386

  • HTTP/2 graceful shutdown timeout is correctly set using ClientFactory.idleTimeoutMillis(). #4381

  • Unframed GrpcServices now support application/x-protobuf and application/x-google-protobuf media types. #4355 #4364

  • You can now use a custom JsonFormat.Printer for ProtobufResponseConverterFunction. #4301 #4336

📃 Documentation

  • You can now learn how to write a gRPC service and client with Armeria by walking through the gRPC tutorial. #3987
    • Special thanks to @freevie who volunteered for this.

🏚️ Deprecations

☢️ Breaking changes

⛓ Dependencies

  • Brave 5.13.10 → 5.13.11
  • Brotli4J 1.7.1 → 1.8.0
  • Dropwizard Metrics 4.2.10 → 4.2.12
  • gRPC-Java 1.48.0 → 1.49.0
  • Jackson 2.13.3 → 2.13.4
  • Micrometer 1.9.2 → 1.9.3
  • Reactor 3.4.21 → 3.4.22
  • Sangria 3.0.1 → 3.2.0
  • Scala 3.1.3 → 3.2.0
  • Spring Boot 2.7.2 → 2.7.3

🙇 Thank you