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")@ProducesJsonSequencespublic Publisher<MyProtobufMessage> protobufJsonSeqPublisher() {return StreamMessage.of(MyProtobufMessage.newBuilder()...build(),MyProtobufMessage.newBuilder()...build());}@Get("/items")@ProducesJsonSequencespublic 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) {...@Overrideprotected 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:

























