0.97.0 release notes
8th December 2019
🌟 New features
We now have a new immutable cookie API with
SameSite
attribute support, which replaces Netty's cookie API. #1567 #2286Cookie c1 = Cookie.of("foo", "bar") Cookie c2 = Cookie.builder("alice", "bob") .domain("foo.com") .path("/bar") .maxAge(3600) .sameSite("Strict") .build(); // Encoding ResponseHeaders resHeaders = ResponseHeaders.builder(HttpStatus.OK) .add(HttpHeaderNames.SET_COOKIE, Cookie.toSetCookieHeaders(c1, c2)) .build(); // Decoding Cookies cookies = Cookie.fromSetCookieHeaders( resHeaders.getAll(HttpHeaderNames.SET_COOKIE));
You can now replace the request headers of
HttpRequest
more conveniently usingHttpRequest.withHeaders(RequestHeaders)
. #2278 #2283HttpRequest req = HttpRequest.of(HttpMethod.POST, "/post", MediaType.PLAIN_TEXT_UTF_8, "Hello!"); RequestHeaders newHeaders = RequestHeaders.of(HttpMethod.PUT, "/put", MediaType.PLAIN_TEXT_UTF_8); HttpRequest newReq = req.withHeaders(newHeaders);
ServiceRequestContext.blockingTaskExecutor()
is now aScheduledExecutorService
instead ofExecutorService
, which means you can schedule delayed or periodic tasks. #2269// A service that sends `PING!` every second. HttpService service = (ctx, req) -> { HttpResponse res = HttpResponse.streaming(); res.write(ResponseHeaders.of(200)); AtomicReference<ScheduledFuture<?>> futureHolder = new AtomicReference<>(); futureHolder.set(ctx.blockingTaskExecutor().scheduleWithFixedDelay(() -> { boolean success = res.tryWrite(HttpData.ofUtf8("PING!\r\n")); if (!success) { if (futureHolder.get() != null) { futureHolder.get().cancel(false); } } }, 1, TimeUnit.SECONDS)); return res; };
You can now add converters and exception handlers to annotated services more conveniently using the new fluent builder methods. #2242
Server server = Server.builder() .annotatedService().requestConverters(converterA, converterB) .responseConverters(converterC, converterD) .exceptionHandlers(handlerA, handlerB) .build(myAnnotatedService) .build();
You can now configure how Armeria decides
ServiceRequestContext.clientAddress()
viaServerBuilder.clientAddressMapper()
. #1631 #2294Server.builder() .clientAddressMapper(proxiedAddrs -> { InetSocketAddress srcAddr = proxiedAddrs.sourceAddress(); List<InetSocketAddress> destAddrs = proxiedAddrs.destinationAddresses(); if (destAddrs.isEmpty()) { // No proxy servers involved. return srcAddr; } else { // When there are more than one proxy server involved, // trust only the address given by the last proxy server. return destAddrs.get(destAddrs.size() - 1); } });
You can now choose a different SLF4J
Logger
than the default one when usingLoggingClient
orLoggingService
. #2220 #2237Logger myLogger = LoggerFactory.getLogger(MyService.class); Server server = Server.builder() .service("/", myService.decorate(LoggingService.builder() .logger(myLogger) .newDecorator())) .build();
BraveClient
now adds connection timing information to a span. #2271 #2273connection-acquire.start
andconnection-acquire.end
dns-resolve.start
anddns-resolve.end
socket-connect.start
andsocket-connect.end
connection-reuse.start
andconnection-reuse.end
withDeadlineAfter
is now supported for gRPC client stubs. #2284GrpcService
now allows controlling whether to respect the"grpc-timeout"
header sent by a gRPC client. #2284- Consider disabling this feature via
GrpcServiceBuilder.useClientTimeoutHeader()
in an insecure environment.
- Consider disabling this feature via
THttpService
andThriftCallService
now supports adding more than one Thrift service at the same endpoint. #2164 #2285class MyFooService implements FooService.Iface ... { public void foo() { ... } } class MyBarService implements BarService.Iface ... { public void bar() { ... } } // "foo" call goes to MyFooService and // "bar" call goes to MyBarService. Server server = Server.builder() .service("/thrift", THttpService.builder() .addService(new MyFooService()) .addService(new MyBarService()) .build()) .build();
armeria-spring-boot-actuator-autoconfigure
now supports the official CORS settings. #2063# application.yml management.endpoints.web.cors.allowed-origins: https://example.com management.endpoints.web.cors.allowed-methods: GET,POST
📈 Improvements
- Improved routing performance. #2293 #2295
- The core JAR file is now smaller, by removing unnecessary resources such as web fonts and favicons. #2299 #2300
HttpRequest.of(RequestHeaders, Publisher)
now handles the case where thePublisher
is actually anHttpRequest
. #2278 #2283
🔒 Security fixes
- (Moderate) CWE-113: Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Response Splitting')
- (Low) Multiple timing attack vulnerabilities leading to the recovery of secrets based on the use of non-constant time compare function
🛠️ Bug fixes
- The event loop threads are properly cleaned up when
Server
failed to start. #2288 Server
now sends a redirect response correctly for the requests without a trailing slash (/
) even when you bound a fallback service atprefix:/
. #2116 #2292- Fixed a bug where routing cache did not work as expected, leading suboptimal routing performance. #2293
- Annotated services now accept only the first
"Cookie"
header when injectingCookies
, because multiple"Cookie"
headers must be prohibited according to RFC 6265. #2286 GrpcService
now respects gRPC stub deadline. #2008 #2284
🏚️ Deprecations
- Deprecated
HttpRequest.of(HttpRequest, RequestHeaders)
in favor ofHttpRequest.withHeaders()
#2283 - Deprecated
HttpRequest.of(AggregatedHttpRequest)
andHttpResponse.of(AggregatedHttpResponse)
in favor ofAggregatedHttpRequest.toHttpRequest()
andAggregatedHttpResponse.toHttpResponse()
. #2283- These methods were removed from
AggregatedHttpMessage
some time ago due to ambiguity, but now we are reviving them because we have separate types for requests and responses.
- These methods were removed from
Cookies.copyOf()
has been deprecated in favor ofCookies.of()
. #2286AnnotatedServiceBuildingBuilder.requestConverter()
,responseConverter()
andexceptionHandler()
have been deprecated in favor ofrequestConverters()
,responseConverters()
andexceptionHandlers()
respectively. #2242Sampler.rateLimited()
has been deprecated in favor ofrateLimiting()
. #2122 #2289THttpService.of()
andofFormats()
factory methods that require aMap<String, ?>
have been deprecated. UseTHttpService.builder()
and callTHttpService.addService(String, Object)
multiple times instead. #2285
☢️ Breaking changes
ServerBuilder.blockingTaskExecutor()
now requires aScheduledExecutorService
instead of anExecutorService
. #2269- The behavior of automatic redirection for the case of missing trailing slash (
/
) has been changed (in a good way). #2116 #2292- For example, previously, when a client sent a request to
/docs
, the following server routed the request toTomcatService
:Server server = Server.builder() .serviceUnder("/docs", new DocService()) .serviceUnder("/", TomcatSerivce.forClassPath(...)) .build();
- However, from this release, the request will not be routed to
TomcatService
but the server will issue a redirect response to/docs/
.
- For example, previously, when a client sent a request to
ProxiedAddresses
class has been redesigned to handle the case of more than one intermediary proxy server. #1631 #2294Cookies
now uses Armeria's ownCookie
. #2286Cookies
now may contain the cookies with the same name. #2286- Note that RFC does not prohibit duplicate names at all, so this is correct behavior.
- Annotated services now accept only the first
"Cookie"
header when decoding cookies. Sending multiple"Cookie"
headers was a violation of RFC 6265 anyway. #2286 - The
ThriftCallService
factory method with signatureof(Map<String, ? extends Iterable<?>>)
has been changed toof(Map<String, ? extends Iterable<?>>)
#2285
⛓ Dependencies
- Brave 1.3.1
- Jetty 9.4.22 -> 9.4.24
- Micrometer 1.3.1 -> 1.3.2
- Netty TCNative BoringSSL 2.0.27 -> 2.0.26
- Project Reactor 3.3.0 -> 3.3.1
- RxJava2 2.2.14 -> 2.2.15
- Spring Boot 2.1.10 -> 2.2.1
- Tomcat 9.0.27 -> 9.0.29, 8.5.47 -> 8.5.49