Skip to main content
info

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

v1.14.0

January 27, 2022

🌟 New features

  • You can now use Scala 3 for armeria-scala and armeria-scalapb. #3614 #4036

    tip

    Scala 3 modules are currently experimental. We're looking forward to your feedback.

  • You can now fluently convert an HttpResponse into a desired type using WebClient. #4021

    WebClient client = WebClient.of("https://api.example.com");
    CompletableFuture<ResponseEntity<MyObject>> response =
    client.prepare()
    .get("/v1/items/1")
    .asJson(MyObject.class)
    .execute();
  • You can now use BlockingWebClient to wait for an HttpResponse to be completed. #4021

    BlockingWebClient client = BlockingWebClient.of("https://api.example.com");
    ResponseEntity<MyObject> response =
    client.prepare()
    .get("/v1/items/1")
    .asJson(MyObject.class)
    .execute();
    tip

    Note that you should never use this client in an EventLoop thread. Use it from a non-EventLoop thread such as BlockingTaskExecutor.

  • You can now fluently build a gRPC client with various options using GrpcClientBuilder. #3981 #3999 #3964 #3975

    GrpcClients
    .builder(...)
    .decorator(myDecorators)
    .maxResponseMessageLength(MAX_MESSAGE_SIZE)
    .jsonMarshallerFactory(descriptor -> {
    ...
    })
    .intercept(myInterceptors)
    .build(MyStub.class);
  • You can now fluently build an HttpResponse by using HttpResponseBuilder. #3398 #3941

    HttpResponse
    .builder()
    .ok()
    .header(HttpHeaderNames.USER_AGENT, "Armeria")
    .content(content)
    .build();
  • You can now use StreamMessage.mapAsync() to transform a StreamMessage using an async operation. #3916 #3962

    StreamMessage<Integer> userIds = StreamMessage.of(...);
    StreamMessage<UserInfo> userInfos = userIds.mapAsync(this::getUserInfo);

    CompletableFuture<UserInfo> getUserInfo(int userId) {
    ...
    }
  • You can now use HttpResponse.peekHeaders(), HttpMessage.peekData(), HttpMessage.peekTrailers() and StreamMessage.peekError() operators on HttpRequest and HttpResponse. #3097 #3988 #3949

    HttpRequest request =
    HttpRequest.of(RequestHeaders.of(HttpMethod.POST, "/items"),
    HttpData.ofUtf8("data1,data2"))
    HttpHeaders.of("trailer", "foo"));
    HttpRequest peeked =
    request.peekData(data -> {
    assert data.toStringUtf8().equals("data1,data2");
    }).peekTrailers(trailers -> {
    assert trailers.get("trailer").equals("foo");
    });

    HttpRequest failed =
    HttpRequest.ofFailure(new IllegalStateException("Something went wrong."));
    HttpRequest peeked = failed.peekError(cause -> {
    assert cause instanceof IllegalStateException;
    });
  • You can now generate an error response differently in ServerErrorHandler depending on the RequestHeaders. #4037

  • You can now use {*var} and :*var path patterns to capture rest paths of a request. #3031 #3997

    Server
    .builder()
    .service("/api/{item}/route/{*contents}", (ctx, req) -> {
    // If a request path is "/api/123/route/foo/bar/baz",
    // '*contents' should be foo/bar/baz
    assert ctx.pathParam("contents").equals("foo/bar/baz");
    });
  • You can now use ServiceRequestContext.queryParams() to get the decoded query parameters. #3955 #3960

  • You can now send query parameters using QueryParams with WebClient. #3915 #3921

    QueryParams params = QueryParams.of("foo", "bar");
    WebClient client = ...;
    // Sends 'GET /api/items/1?foo=bar'
    client.get("/api/items/1", params);
    // Sends 'POST /api/items?foo=bar' with body '...'
    client.post("/api/items", params, content);
  • You can now write a StreamMessage<HttpData> into a file using StreamMessages.writeTo(). #3870 #3874

    StreamMessage<HttpData> stream = ...;
    Path destination = ...;
    StreamMessages.writeTo(stream, destination);
  • You can now force a state transition of a CircuitBreaker by using CircuitBreaker.enterState(). #4010 #4022

    CircuitBreaker circuitBreaker = CircuitBreaker.of(...);
    circuitBreaker.enterState(CircuitState.FORCED_OPEN);
  • You can now conveniently create a RetryRule and RetryRuleWithContent by specifying a Backoff. #3875 #2899 #3895

    RetryRule.failsafe(Backoff.fixed(1000));
    RetryRuleWithContent.onException(Backoff.fixed(2000));
  • You can now dynamically change the maximum number of concurrent active requests of a ConcurrencyLimitingClient. #3842 #3985

    ConcurrencyLimitingClient.newDecorator(new DynamicLimit());

    class DynamicLimit implements IntSupplier {
    @Override
    public int getAsInt() {
    // Dynamically returns the current limitation.
    ...
    }
    }
  • You can now split an HttpRequest into RequestHeaders, StreamMessage<HttpData> and trailers using HttpRequest.split(). #3924 #3953

    HttpRequest request = HttpRequest.of(...);
    SplitHttpRequest splitRequest = request.split();

    RequestHeaders headers = splitRequest.headers();
    StreamMessage<HttpData> body = splitRequest.body();
    CompletableFuture<HttpHeaders> trailers = splitRequest.trailers();
  • Added more well-known MIME type constants to MediaType. #4040

  • You can now customize the MediaType of a file when using FileService. #4001 #4009

    FileService
    .builder(resource)
    .mediaTypeResolver((path, contentEncoding) -> {
    if (path.endsWith(".proto")) {
    return MediaType.PROTOBUF;
    }
    if (path.endsWith(".csv")) {
    return MediaType.CSV_UTF_8;
    }
    // Pass to the default resolver.
    return null;
    })
    .build();
  • You can now send requests to a test server by using ServerExtension.webClient() or ServerRule.webClient(). #3761 #3890

     @RegisterExtension
    static ServerExtension server = new ServerExtension() {
    @Override
    protected void configure(ServerBuilder sb) {
    ...
    }
    };

    @Test
    void test() {
    WebClient client = server.webClient(cb -> { // 👈👈👈
    cb.decorator(LoggingClient.newDecorator());
    });
    client.get("/foo").aggregate().join();
    }
  • You can now export metrics as OpenMetrics format using PrometheusExpositionService. #3926 #3928

  • You can now use UnframedGrpcStatusMappingFunction to customize how a gRPC status is mapped to an HttpStatus when using an unframed gRPC service. #3683 #3948

    UnframedGrpcStatusMappingFunction mappingFunction =
    (ctx, grpcStatus, cause) -> {
    if (grpcStatus.getCode() == DEADLINE_EXCEEDED) {
    return INTERNAL_SERVER_ERROR;
    }
    // Pass to the default mapping function.
    return null;
    };

    UnframedGrpcErrorHandler errorHandler =
    UnframedGrpcErrorHandler.ofJson(mappingFunction);

    GrpcService
    .builder()
    .unframedGrpcErrorHandler(errorHandler);
  • You can now expose WebOperations of Actuator to a port of internal services. #3919 #3946

    armeria:
    ...
    internal-services:
    # Actuator will be exposed to 18080 port.
    include: actuator, health
    port: 18080
  • Added JettyServiceBuilder.tlsReverseDnsLookup() that makes JettyService perform a reverse DNS lookup for each TLS connection, which may be useful for servlets that assume ServletRequest.getRemoteHost() will return a remote host name rather than a remote IP address.

  • Added JettyServiceBuilder.httpConfiguration() that allows a user to set Jetty HttpConfiguration in a type-safe manner.

📈 Improvements

🛠️ Bug fixes

🏚️ Deprecations

☢️ Breaking changes

⛓ Dependencies

  • Brave 5.13.3 → 5.13.7
  • Bouncy Castle 1.69 → 1.70
  • Bucket4J 6.3.0 → 7.0.0
  • Caffeine 2.9.2 → 2.9.3
  • Dropwizard 2.0.25 → 2.0.28
  • Dropwizard Metrics 4.2.4 → 4.2.7
  • gRPC Java 1.41.1 → 1.43.2
  • gRPC Kotlin 1.1.0 → 1.2.1
  • Jackson 2.13.0 → 2.13.1
  • java-jwt 3.18.2 → 3.18.3
  • SLF4J 1.7.32 → 1.7.34
  • jboss-logging 3.4.2 → 3.4.3
  • Kafka 3.0.0 → 3.1.0
  • Kotlin 1.5.32 → 1.6.10
  • Logback 1.2.7 → 1.2.10
  • Micrometer 1.7.6 → 1.8.2
  • Netty 4.1.70 → 4.1.73
    • io_uring 0.0.1 → 0.0.11
  • Prometheus 0.12.0 → 0.14.1
  • protobuf-jackson 1.2.0 → 2.0.0
  • protobuf-java 3.17.3 → 3.19.2
  • Reactor 3.4.12 → 3.4.14
  • RESTEasy 4.7.3 → 5.0.2
  • Scala 2.13.7 → 2.13.8, ⓧ → 3.1.1
  • scala-collection-compat 2.5.0 → 2.6.0
  • ScalaPB 0.11.6 → 0.11.8
  • Spring 5.3.13 → 5.3.15
  • Spring Boot 2.5.7 → 2.6.3
  • Tomcat 9.0.55 → 9.0.56

🙇 Thank you

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

@ks-yim@chris-ryan-square@AmosDoan@tobias-@LiYing2010@KarboniteKream@freevie@klurpicolo@ikhoon@minwoox@gurudatta-carbon@arhont375@wasifaleem@jupiny@hyangtack@injae-kim@ghkim3221@icepeppermint@nvidyala@di-seo@lan17@woohhan@jrhee17@kojilin@anuraaga@heowc@trustin@ta7uw@sleeplesslord@AngerM@policeman-kh

Like Armeria?
Star us ⭐️

×