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
- See Scala integration for the full features.
-
You can now create a
StreamMessagefrom aPathor aFile. #3344Path path = Paths.get("...");
StreamMessage<HttpData> publisher = StreamMessage.of(path); -
You can now filter or transform values of a
StreamMessageusingStreamMessage.filter()orStreamMessage.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
RpcResponseinTHttpService. #3349 #3383THttpService.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 #3357WebClient 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
DecodingClientusing theDecodingClientBuilder. #3348 #3372DecodingClient.builder()
.autoFillAcceptEncoding(false)
.strictContentEncoding(true)
.newDecorator(); -
You can now fluently build an
HttpRequestwith aPublisher<HttpData>. #3343StreamMessage<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 #3366Server.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);
}); -
BraveServicedoes not trace requests forTransientServicesuch asHealthCheckServiceanymore. #3382- You should specify
TransientServiceOption.WITH_TRACINGif you want to trace them.
HealthCheckService.builder()
.transientServiceOptions(WITH_TRACING)
...
.build(); - You should specify
-
You can now clean up resources by overriding
FilteredStreamMessage.onCancellation()whenSubscription.cancel()is called. #3375new 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_idBuiltInPropertyto log the ID ofRequestContext.root(). #3429 #3433 -
You can now use Thrift 0.14.0 with the new
armeria-thrift0.14module. #3470 #3422 -
You can now use OAuth 2.0 related features with the new
armeria-oauth2module. #2268 #2840- OAuth 2.0 server-side token authorization using
OAuth2TokenIntrospectionAuthorizer - Client credential grants using
OAuth2ClientCredentialsGrant - Resource Owner Password Credentials Grant
using
OAuth2ResourceOwnerPasswordCredentialsGrant - OAuth 2.0 Token Revocation using
TokenRevocation
tipOAuth 2.0 features are currently experimental, so you should be careful using the feature.
- OAuth 2.0 server-side token authorization using
-
You can now use Jakarta RESTful Web Services on top of Armeria with the
armeria-resteasymodule. #3285 #3296tiparmeria-resteasyis currently experimental, so you should be careful using the feature.
📈 Improvements
🛠️ Bug fixes
- You no longer see
NullPointerExceptioninHttpResponseDecoder. #3036 #3407 - You no longer see
405 Method Not Allowedwhen the exact and param path are defined with different HTTP methods. #3330 #3340 - You no longer see
Address family not supported by protocolorConnection refusederror 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:fullcameloption of Thrift compiler. #3269 #3360 #3369 SmartLifecyclefor 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
CancellationExceptionwhen anHttpResponseis fully consumed on server-side. #3387 - You no longer see
ClosedSteamExceptionin Jetty service when it fails to write to anHttpResponse.EofExceptionis raised instead. #3412 - You no longer see
IllegalStateExceptionwhenRequestContextHooksis enabled. #3441 #3442 ArmeriaServerHttpResponseof Spring Webflux integration now correctly propagates Reactor'sContext. #3439 #3443
☢️ Breaking changes
-
ServiceRequestContextis added to the parameter ofhandleMessageinAbstractUnaryGrpcService. #3403- The return type is also changed to
CompletionStage. #3409
protected abstract CompletionStage<byte[]> handleMessage(
ServiceRequestContext ctx, byte[] message);
protected final CompletionStage<ByteBuf> handleMessage(
ServiceRequestContext ctx, ByteBuf message) {...} - The return type is also changed to
-
GrpcStatusFunctionnow extendsBiFunction<Throwable, Metadata, Status>. #3329 -
The
numberStepsmethod inWeightRampingUpStrategyBuilderis now changed tototalSteps. #3377
⛓ 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:

























