Skip to main content
info

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

v1.12.0

October 7, 2021

🌸 Highlights

  • You can now enable HTTP/JSON to GRPC transcoding in your GrpcService. #3786

    GrpcService.builder()
    .addService(helloService)
    .enableHttpJsonTranscoding(true) // 👈👈👈
    .build();
    tip

    HTTP/JSON transcoding is currently experimental. We're looking forward to your feedback.

🌟 New features

  • You can now bind a virtual host to a specific port. #3798 #3833

    Server.builder()
    .http(8080)
    .http(18080)
    // Bind the virtual host to 18080 port.
    .virtualHost(18080).service("/health", healthCheckService) // 👈👈👈
    .serviceUnder("/secret/", secretService)
    .and()
    ...
  • You can now customize any HTTP error status responses including a decoder-level error and HttpStatusException. #3822 #3853

    ServerBuilder sb = ...
    sb.errorHandler(new ServerErrorHandler() {
    @Override
    public HttpResponse onServiceException(ServiceRequestContext ctx, Throwable cause) {
    // Convert an error that is raised in a service or a decorator.
    if (cause instanceof IllegalArgumentException) {
    return HttpResponse.of(HttpStatus.BAD_REQUEST);
    }
    ...
    }

    @Override
    public AggregatedHttpResponse onProtocolViolation(ServiceConfig config,
    HttpStatus status,
    @Nullable String description,
    @Nullable Throwable cause) {
    // Convert the decoder-level error.
    if (status == HttpStatus.REQUEST_ENTITY_TOO_LARGE) {
    return AggregatedHttpResponse.of(HttpStatus.BAD_REQUEST);
    }
    ...
    }

    @Override
    public AggregatedHttpResponse renderStatus(ServiceConfig config,
    HttpStatus status,
    @Nullable String description,
    @Nullable Throwable cause) {
    // Convert the given error status and cause.
    ...
    }
    })
  • You can now share ConcurrencyLimit between ConcurrencyLimitingClient. #3478 #3753 #3860

    ConcurrencyLimit limit = ConcurrencyLimit.of(64); // Can send 64 requests concurrently.

    WebClient client1 = WebClient
    .builder(...)
    .decorator(ConcurrencyLimitingClient.newDecorator(limit))
    .build(...);

    WebClient client2 = WebClient
    .builder(...)
    .decorator(ConcurrencyLimitingClient.newDecorator(limit))
    // different options
    ...
    .build(...);
  • You can now allow all headers in Access-Control-Request-Headers from a preflight request using CorsServiceBuilder.allowAllRequestHeaders(). #3839 #3843

    ServerBuilder sb = ...
    sb.service("/cors", myService.decorate(CorsService.builder("http://example.com")
    .allowRequestMethods(HttpMethod.GET)
    .allowAllRequestHeaders(true) // 👈👈👈
    .newDecorator()));
  • You can now sanitize the preview of a request or a response. #3791 #3795

    ContentPreviewingService.builder(ContentPreviewerFactory.text(100))
    .requestPreviewSanitizer(requestPreviewSanitizer) // 👈👈👈
    .responsePreviewSanitizer(responsePreviewSanitizer) // 👈👈👈
    .newDecorator();
  • A compressed file is automatically decompressed if FileServiceBuilder.autoDecompress() is set to true. #3804 #3810

    FileService.builder(FileServiceBuilderTest.class.getClassLoader(), "/")
    .serveCompressedFiles(true) // This should be set to true to serve compressed files.
    .autoDecompress(true) // 👈👈👈
    .build();
  • You can now send a delayed HttpResponse using HttpResponse.delayed(). #3698 #3770

    Supplier<HttpResponse> responseSupplier = ...
    HttpResponse.delayed(responseSupplier, Duration.ofSeconds(5)); // 👈👈👈
  • You can now specify different blocking task executors for different HttpService using ServiceBindingBuilder. #3740 #3754

    Server.builder()
    .route()
    .get("/foo")
    .blockingTaskExecutor(fooExecutor) // 👈👈👈
    .build(fooService)
    .route()
    .get("/bar")
    .blockingTaskExecutor(barExecutor) // 👈👈👈
    .build(barService)
    .build()
  • You can now specify an AuthToken when building a HealthCheckedEndpointGroup. #3809

    HealthCheckedEndpointGroup.builder(endpointGroup, "/health")
    .auth(AuthToken.ofOAuth2("..."))
    .build();
  • You can now export different properties for different RequestContextExportingAppenders to log with Logback. #3775 #3769

    // logback.xml
    <appender name="RCEA1" class="com.linecorp.armeria.common.logback.RequestContextExportingAppender">
    ...
    <export>remote.ip</export>
    </appender>

    <appender name="RCEA2" class="com.linecorp.armeria.common.logback.RequestContextExportingAppender">
    ...
    <export>remote.port</export>
    </appender>
  • You can now log HTTP/2 frames using the frame loggers. #3817

    <!-- logback.xml -->
    <logger name="com.linecorp.armeria.logging.traffic.server.http2" level="TRACE" />
    <logger name="com.linecorp.armeria.logging.traffic.client.http2" level="TRACE" />

gRPC

  • You can now use UnframedGrpcErrorHandler to map a non-OK gRPC status to an HttpResponse in an unframed gRPC service. #3777 #3782
    GrpcService.builder()
    .addService(grpcService)
    .enableUnframedRequests(true) // This should be set to true to enable UnframedGrpcService.
    .unframedGrpcErrorHandler(UnframedGrpcErrorHandler.of()) // 👈👈👈
    .build();

Kotlin

  • You can now use Kotlin Flow as a return type in an annotated service. #3756 #3768
    @Get("/members")
    @ProducesJson
    fun listMembers() = flow { // 👈👈👈
    emit(Member(name = "foo", age = 10))
    emit(Member(name = "bar", age = 20))
    emit(Member(name = "baz", age = 30))
    }

Spring integration

  • You can now configure an internal port with a Spring property file. #3798 #3833
    armeria:
    ports:
    - port: 8080
    internal-services:
    # Prometheus exposition and health check service will be
    # exposed to 18080 port. The requests to 8080 port will return 404 Not Found.
    include: metrics, health
    port: 18080 # 👈👈👈
    protocols: http
  • You can now specify all parameters of ServerBuilder with a Spring property file. #3705 #3735
    armeria:
    ports:
    - port: 0
    max-num-connections: 8
    idle-timeout: 2s
    ping-interval: 1s
    max-connection-age: 4s
    max-num-requests-per-connection: 4
    http2-initial-connection-window-size: 2mb
    http2-initial-stream-window-size: 4mb
    http2-max-streams-per-connection: 1
    ...

GraphQL

  • You can now specify a GraphQLSchema or schemaUrls when building a GraphQlService. #3708 #3815
    GraphQLSchema schema = ...
    sb.service("/graphql", GraphqlService.builder()
    .schema(schema) // 👈👈👈
    ...
    .build());

📃 Documentation

📈 Improvements

🛠️ Bug fixes

🏚️ Deprecations

☢️ Breaking changes

⛓ Dependencies

  • Bucket4j 6.2.0 → 6.3.0
  • Dropwizard 2.0.24 → 2.0.25
  • Dropwizard metrics 4.2.3 → 4.2.4
  • GraphQL-Java 17.1 → 17.3
  • gRPC-Java 1.40.1 → 1.41.0
  • Jackson 2.12.4 → 2.13.0
  • java-jwt 3.18.1 → 3.18.2
  • Kotlin 1.5.10 → 1.5.21
  • Kafka 2.8.0 -> 3.0.0
  • Kotlin 1.5.21 → 1.5.31
  • Kotlin Coroutines 1.5.1 → 1.5.2
  • Logback 1.2.5 → 1.2.6
  • Micrometer 1.7.3 → 1.7.4
  • Netty 4.1.66 → 4.1.68
  • netty-tcnative-boringssl-static 2.0.40 → 2.0.43
  • Prometheus simpleclient 0.11.0 → 0.12.0
  • Reactor 3.4.9 → 3.4.10
  • RESTEasy 4.7.1 → 4.7.2
  • RxJava 3.1.0 → 3.1.1
  • scala-java8-compat 1.0.0 → 1.0.1
  • Spring 5.3.9 → 5.3.10
  • Spring Boot 2.5.4 → 2.5.5
  • Thrift 0.14.2 → 0.15.0
  • Tomcat 9.0.46 → 9.0.53

🙇 Thank you

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

@fantayeneh@jasper-vandemalle@trustin@TheWeaVer@ghkim3221@berry120@ikhoon@kezhenxu94@freevie@anuraaga@heowc@ks-yim@minwoox@vivekkothari@jrhee17

Like Armeria?
Star us ⭐️

×