Skip to main content
info

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

v1.6.0

April 7, 2021

🌟 New features

  • You can now send and handle multipart requests and responses. #253 #3327

    // Send a multipart message.
    WebClient client = WebClient.of("http://example.com/");
    BodyPart part1 = BodyPart.of(ContentDisposition.of("form-data", "username"), "Armeria");
    BodyPart part2 = BodyPart.of(ContentDisposition.of("form-data", "password"), "mypassword");
    Multipart multipart = Multipart.of(part1, part2);
    client.execute(multipart.toHttpRequest("/login"));

    // Handle a multipart message.
    Server.builder()
    .service("/login", (ctx, req) -> {
    Multipart multipart = Multipart.from(req);
    multipart.aggregate().thenApply(aggregated -> {
    assert aggregated.field("username").contentUtf8().equals("Armeria");
    assert aggregated.field("password").contentUtf8().equals("mypassword");
    ...
    }));
    });
  • Armeria now provides various useful extensions and conversions for Scala. #3395

  • You can now create a StreamMessage from a Path or a File. #3344

    Path path = Paths.get("...");
    StreamMessage<HttpData> publisher = StreamMessage.of(path);
  • You can now filter or transform values of a StreamMessage using StreamMessage.filter() or StreamMessage.map(). #3351

    // Filter.
    StreamMessage<Integer> source = StreamMessage.of(1, 2, 3, 4, 5);
    StreamMessage<Integer> even = source.filter(x -> x % 2 == 0);

    // Transform.
    StreamMessage<Integer> source = StreamMessage.of(1, 2, 3, 4, 5);
    StreamMessage<Boolean> isEven = source.map(x -> x % 2 == 0);
  • You can now send a different response depending on the exception. #3209 #3413

    Server.builder().exceptionHandler((ctx, cause) -> {
    if (cause instanceof RequestTimeoutException) {
    // The request timed out!
    return AggregatedHttpResponse.of(...);
    }

    // Return null to let ExceptionHandler.ofDefault() convert the exception.
    return null;
    })...
  • You can now convert an exception into an RpcResponse in THttpService. #3349 #3383

    THttpService.builder()
    .addService("hello", helloService)
    .exceptionHandler((ctx, cause) -> {
    if (cause instanceof IllegalArgumentException) {
    return RpcResponse.of(new CustomizedException("Bad Request!"));
    }
    ...
    })
  • You can now set a response timeout and attributes using WebClientRequestPreparation. #3347 #3357

    WebClient client = ...;
    client.prepare()
    .get("/my-service")
    .responseTimeout(Duration.ofSeconds(3))
    .attr(USER_ID, userId)
    .attr(USER_SECRET, secret)
    .execute();
  • You can now specify additional error details to a gRPC response when an exception is raised in a gRPC service. #3307 #3329

    GrpcService.builder()
    .exceptionMapping((cause, metadata) -> {
    if (throwable instanceof AuthError) {
    metadata.put(KEY, toMetadata(cause))
    return Status.UNAUTHENTICATED.withCause(cause);
    }
    ...
    })
  • You can now customize the success condition of a metric. #3404 #3410

    MetricCollectingService.builder(...)
    .successFunction((context, log) -> {
    final int statusCode = log.responseHeaders().status().code();
    return (statusCode >= 200 && statusCode < 400) || statusCode == 404;
    });
  • You can now fluently build a DecodingClient using the DecodingClientBuilder. #3348 #3372

    DecodingClient.builder()
    .autoFillAcceptEncoding(false)
    .strictContentEncoding(true)
    .newDecorator();
  • You can now fluently build an HttpRequest with a Publisher<HttpData>. #3343

    StreamMessage<HttpData> publisher = StreamMessage.of(...);
    HttpRequest.builder()
    .method(HttpMethod.GET)
    .path("/")
    .content(MediaType.PLAIN_TEXT_UTF_8, publisher)
    .build();
  • You can now convert a stream of Protobuf Messages into JSON Text sequences using an annotated service. #3394

    @Get("/items")
    @ProducesJsonSequences
    public Publisher<MyProtobufMessage> protobufJsonSeqPublisher() {
    return StreamMessage.of(MyProtobufMessage.newBuilder()...build(),
    MyProtobufMessage.newBuilder()...build());
    }

    @Get("/items")
    @ProducesJsonSequences
    public Stream<MyProtobufMessage> protobufJsonSeqPublisher() {
    return Stream.of(MyProtobufMessage.newBuilder()...build(),
    MyProtobufMessage.newBuilder()...build());
    }
  • You can now customize the default service name of a RequestLog. #3232 #3366

    Server.builder()
    .defaultServiceNaming(ctx -> {
    final ServiceConfig config = ctx.config();
    return config.route().patternString();
    });
    // For a specific service.
    Server.builder()
    .route().path("/")
    .defaultServiceNaming(...)
    ...
  • You can now check if the current request is matched by any routes or not. #3365 #3378

    ServerBuilder sb = ...
    sb.decorator((delegate, ctx, req) -> {
    if (ctx.config().route().isFallback()) {
    // This request is not matched any routes.
    }
    return delegate.serve(ctx, req);
    });
  • BraveService does not trace requests for TransientService such as HealthCheckService anymore. #3382

    HealthCheckService.builder()
    .transientServiceOptions(WITH_TRACING)
    ...
    .build();
  • You can now clean up resources by overriding FilteredStreamMessage.onCancellation() when Subscription.cancel() is called. #3375

    new MyFilteredHttpResponse(res) {
    ...
    @Override
    protected void onCancellation(Subscriber<? super U> subscriber) {
    // Clean up resources.
    }
    }
  • You can now easily set 'cookie' or 'set-cookie' headers. #3388 #3391

    Cookie cookie = Cookie.of("cookie", "value");
    RequestHeaders headers = RequestHeaders.builder(HttpMethod.GET, "/")
    .cookie(cookie)
    .build();
    assert headers.cookies().equals(Cookies.of(cookie));

    Cookie setCookie1 = ...
    Cookie setCookie2 = ...
    ResponseHeaders headers = ResponseHeaders.builder(HttpStatus.OK)
    .cookies(setCookie1, setCookie2)
    .build();
    assert headers.cookies().equals(Cookies.of(setCookie1, setCookie2));
  • You can now use req.root_id BuiltInProperty to log the ID of RequestContext.root(). #3429 #3433

  • You can now use Thrift 0.14.0 with the new armeria-thrift0.14 module. #3470 #3422

  • You can now use OAuth 2.0 related features with the new armeria-oauth2 module. #2268 #2840

    tip

    OAuth 2.0 features are currently experimental, so you should be careful using the feature.

  • You can now use Jakarta RESTful Web Services on top of Armeria with the armeria-resteasy module. #3285 #3296

    tip

    armeria-resteasy is currently experimental, so you should be careful using the feature.

📈 Improvements

🛠️ Bug fixes

  • You no longer see NullPointerException in HttpResponseDecoder. #3036 #3407
  • You no longer see 405 Method Not Allowed when the exact and param path are defined with different HTTP methods. #3330 #3340
  • You no longer see Address family not supported by protocol or Connection refused error anymore on certain machines with IPv6 enabled. #3425
  • The Unicode characters like emojis in a JSON response are now rendered correctly in DocService. #3396
  • You no longer see the wrong response body when the payload violates the protocol or is too large. #3419
  • You can now use the Thrift client and service that is generated by java:fullcamel option of Thrift compiler. #3269 #3360 #3369
  • SmartLifecycle for Armeria server graceful shutdown is only created when the Armeria server is created by Spring integration. #3300
  • You can now use Sealed oneofs message from ScalaPB with JSON for gRPC and annotated services. #3342 #3394
  • You no longer see CancellationException when an HttpResponse is fully consumed on server-side. #3387
  • You no longer see ClosedSteamException in Jetty service when it fails to write to an HttpResponse. EofException is raised instead. #3412
  • You no longer see IllegalStateException when RequestContextHooks is enabled. #3441 #3442
  • ArmeriaServerHttpResponse of Spring Webflux integration now correctly propagates Reactor's Context. #3439 #3443

☢️ Breaking changes

⛓ Dependencies

  • Bucket4J 4.10.0 → 6.2.0
  • Curator 4.3.0 → 5.1.0
    • ZooKeeper 3.5.8 → 3.6.2
  • Dropwizard 2.0.18 → 2.0.20
  • Dropwizard Metrics 4.1.17 → 4.1.18
  • gRPC 1.35.0 → 1.36.1
  • Jackson 2.12.0 → 2.12.2
  • java-jwt 3.12.1 → 3.14.0
  • Jetty 9.4.36 → 9.4.39
  • Micrometer 1.6.3 → 1.6.5
  • Netty 4.1.58 → 4.1.63
    • Netty TCNative BoringSSL 2.0.36 → 2.0.38
    • Netty io_uring transport 0.0.3 → 0.0.5
  • OpenSAML 3.4.5 → 3.4.6
    • Shibboleth java-support 7.5.1 → 7.5.2
  • Reactor 3.4.2 → 3.4.4
    • Reactor Kotlin extensions 1.1.2 → 1.1.3
  • RxJava 3.0.9 → 3.0.11, 2.2.20 → 2.2.21
  • ScalaPb 0.10.10 → 0.11.0
  • Spring Boot 2.4.2 → 2.4.4
  • Tomcat 9.0.41 → 9.0.44, 8.5.61 → 8.5.64

🙇 Thank you

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

@hyangtack@szeiger@policeman-kh@eugene70@kojilin@hexoul@heka1024@andrey-tpt@ks-yim@ateirney@mauhiz@ghkim3221@ikhoon@richieyan@selectAll@Waynefn@okue@JunoJunho@anuraaga@probepark@heowc@eisig@trustin@nirvanarsc@minwoox@max904-github

Like Armeria?
Star us ⭐️

×