1.14.0 release notes
27th January 2022
🌟 New features
You can now use Scala 3 for
armeria-scala
andarmeria-scalapb
. #3614 #4036You can now fluently convert an
HttpResponse
into a desired type usingWebClient
. #4021WebClient 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 anHttpResponse
to be completed. #4021BlockingWebClient client = BlockingWebClient.of("https://api.example.com"); ResponseEntity<MyObject> response = client.prepare() .get("/v1/items/1") .asJson(MyObject.class) .execute();
You can now fluently build a gRPC client with various options using
GrpcClientBuilder
. #3981 #3999 #3964 #3975GrpcClients .builder(...) .decorator(myDecorators) .maxResponseMessageLength(MAX_MESSAGE_SIZE) .jsonMarshallerFactory(descriptor -> { ... }) .intercept(myInterceptors) .build(MyStub.class);
You can now fluently build an
HttpResponse
by usingHttpResponseBuilder
. #3398 #3941HttpResponse .builder() .ok() .header(HttpHeaderNames.USER_AGENT, "Armeria") .content(content) .build();
You can now use
StreamMessage.mapAsync()
to transform aStreamMessage
using an async operation. #3916 #3962StreamMessage<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()
andStreamMessage.peekError()
operators onHttpRequest
andHttpResponse
. #3097 #3988 #3949HttpRequest 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 theRequestHeaders
. #4037You can now use
{*var}
and:*var
path patterns to capture rest paths of a request. #3031 #3997Server .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 #3960You can now send query parameters using
QueryParams
withWebClient
. #3915 #3921QueryParams 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 usingStreamMessages.writeTo()
. #3870 #3874StreamMessage<HttpData> stream = ...; Path destination = ...; StreamMessages.writeTo(stream, destination);
You can now force a state transition of a
CircuitBreaker
by usingCircuitBreaker.enterState()
. #4010 #4022CircuitBreaker circuitBreaker = CircuitBreaker.of(...); circuitBreaker.enterState(CircuitState.FORCED_OPEN);
You can now conveniently create a
RetryRule
andRetryRuleWithContent
by specifying aBackoff
. #3875 #2899 #3895RetryRule.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 #3985ConcurrencyLimitingClient.newDecorator(new DynamicLimit()); class DynamicLimit implements IntSupplier { @Override public int getAsInt() { // Dynamically returns the current limitation. ... } }
You can now split an
HttpRequest
intoRequestHeaders
,StreamMessage<HttpData>
and trailers usingHttpRequest.split()
. #3924 #3953HttpRequest 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
. #4040You can now customize the
MediaType
of a file when usingFileService
. #4001 #4009FileService .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()
orServerRule.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 #3928You can now use
UnframedGrpcStatusMappingFunction
to customize how a gRPC status is mapped to anHttpStatus
when using an unframed gRPC service. #3683 #3948UnframedGrpcStatusMappingFunction 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
WebOperation
s of Actuator to a port of internal services. #3919 #3946armeria: ... internal-services: # Actuator will be exposed to 18080 port. include: actuator, health port: 18080
Added
JettyServiceBuilder.tlsReverseDnsLookup()
that makesJettyService
perform a reverse DNS lookup for each TLS connection, which may be useful for servlets that assumeServletRequest.getRemoteHost()
will return a remote host name rather than a remote IP address.Added
JettyServiceBuilder.httpConfiguration()
that allows a user to set JettyHttpConfiguration
in a type-safe manner.
📈 Improvements
- You can now distinguish services, messages and enums with the same name by their package names from
DocService
UI. #3979 - Improved the memory footprint of
PrometheusExpositionService
by removing large byte array allocations. #3929 - Meters created by
MoreMeters
are now using more accurate percentiles. #4047
🛠️ Bug fixes
- You no longer see
ClassCastException
when duplicating anHttpRequest
or anHttpResponse
. #4030 #4032 - You no longer see
IllegalArgumentException
if an upper-bounded wildcard type with@Param
is used in annotated services. #4007 #4008 - You can now properly get
SameSite
attribute fromCookie.sameSite()
. #3968 #3984 - You no longer see an
EmptyEndpointGroupException
when anEndpointGroup
is successfully resolved with the initialEndpoints
. #3978 HttpRequestBuilder.header()
does not overwrite an old header value anymore. #3932 #3941- A new header value will be appended to the old value.
- You no longer see
413 Request Entity Too Large
when a server receives a cleartext (h2c) upgrade request larger than 16384 bytes. #3859 #3913- The maximum allowed length of an upgrade request will respect
ServerBuilder.maxRequestLength()
orVirtualHost.maxRequestLength()
.
- The maximum allowed length of an upgrade request will respect
- Fixed
Multipart
decoder to correctly publish chunks one by one when streaming a largeBodyPart
. #3774 #3783 - You can now use
google.protobuf.{Struct, Value, ListValue, Any}
messages from yourproto
files when using gRPC-JSON transcoder. #3986 #3992 GrpcService
now automatically disables gRPC-JSON serialization for Protobuf 2 services rather than throwing an exception. #4020 #4033FileService
now keeps query parameters when redirecting to a directory. #4049 #4050- You no longer see
InvocationTargetException
with a Kotlin method whose return type isNothing
in an annotated service. #3961 #4005 - Actuator now correctly shows
Liveness
andReadiness
of a Kubernetes instance. #3031 #3997
🏚️ Deprecations
CircuitBreaker.canRequest()
has been deprecated in favor ofCircuitBreaker.tryRequest()
. #4012Cookie.builder()
andCookie.of()
have been deprecated in favor ofCookie.secureBuilder()
andCookie.ofSecure()
. #3788 #3939GrpcServiceBuilder.setMaxInboundMessageSizeBytes()
andGrpcServiceBuilder.setMaxOutboundMessageSizeBytes()
have been deprecated in favor ofGrpcServiceBuilder.maxRequestMessageLength()
andGrpcServiceBuilder.maxResponseMessageLength()
. #3999AbstractBlockingHttpVfs.get(Executor,String,Clock,String,HttpHeaders)
has been deprecated in favor ofAbstractBlockingHttpVfs.get(Executor,String,Clock,String,HttpHeaders,MediaTypeResolver)
. #4009HttpVfs.get(Executor,String,Clock,String,HttpHeaders)
has been deprecated in favor ofHttpVfs.get(Executor,String,Clock,String,HttpHeaders,MediaTypeResolver)
. #4009
☢️ Breaking changes
HealthChecker.of(Supplier,Duration)
andHealthChecker.of(Supplier,Duration,EventExecutor)
now returnsListenableHealthChecker
instead ofHealthChecker
. #4017ServerErrorHandler.renderStatus()
now requires an additional parameterRequestHeaders
. #4037- This allows a user to generate a different error response depending on the request header value.
⛓ 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