New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Zero copy between Netty's Http2Headers
and Armeria's HttpHeaders
#3817
Conversation
Motivation: Netty's HTTP/2 codec decodes HTTP/2 headers to `Http2Headers` and Armeria HTTP/2 codec converts `Http2Headers` into `RequestHeaders`, `ResponseHeaders` and `HttpHeaders`. It is inefficient since it copies headers two times. After walking through Netty's API, I found out it is possible to set a custom `Http2HeadersDecoder` coverts a headers block into `HttpHeaders` when building `Http2ServerConnectionHandler` Modifications: - Add `ArmeriaHttp2Headers` that implements Netty's `Http2Headers` and internally stores the values in Armeria's `HttpHeaders`. - Manually create `Http2ConnectionEncoder` and `Http2ConnectionDecoder` on both server and client side to customize `Http2HeadersDecoder` - Update `ArmeriaHttpUtil` to directly convert `Http2Headers` to `HttpHeaders` without copies. Result: TBU
Codecov Report
@@ Coverage Diff @@
## master #3817 +/- ##
============================================
+ Coverage 73.24% 73.26% +0.02%
+ Complexity 15082 15052 -30
============================================
Files 1326 1318 -8
Lines 58003 57745 -258
Branches 7356 7330 -26
============================================
- Hits 42486 42309 -177
+ Misses 11774 11681 -93
- Partials 3743 3755 +12
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like the good first step for optimizing HTTP/2 headers. Thanks for looking into this, @ikhoon !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some minor comments.
Nice work!
core/src/main/java/com/linecorp/armeria/common/StringMultimapBuilder.java
Outdated
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/internal/common/ArmeriaHttp2Headers.java
Outdated
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/internal/common/ArmeriaHttp2Headers.java
Outdated
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/internal/common/ArmeriaHttp2Headers.java
Outdated
Show resolved
Hide resolved
outputHeaders.add(name, value); | ||
final HttpHeadersBuilder builder = inputHeaders.toBuilder(); | ||
|
||
for (Entry<AsciiString, AsciiString> disallowed : HTTP_TO_HTTP2_HEADER_DISALLOWED_LIST) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about just using a Set
for HTTP_TO_HTTP2_HEADER_DISALLOWED_LIST
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now, I understand that we use this for case-insensitive comparison.
Let's change the name of the map as @trustin suggested. 😉
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CaseInsensitiveMap
sounds good.
builder.remove(HttpHeaderNames.CONTENT_LENGTH); | ||
} | ||
if (builder == null) { | ||
builder = inputHeaders.toBuilder(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about assigning this builder at the first of the method and remove all if (builder == null)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops... I don't remember why I did like this.🤣
Maybe the code might be changed while fixing test cases.
Any benchmark results? I remember trying an approach to zero copy a long time ago but found it to reduce performance and abandoned it. The approach was probably different than this one though don't remember - would be good to confirm the performance. |
I ran some benchmarks. Unfortunately, I didn't get any enhancement or regression. 😭 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
Motivation:
Netty's HTTP/2 codec decodes HTTP/2 headers to
Http2Headers
andArmeria HTTP/2 codec converts
Http2Headers
intoRequestHeaders
,ResponseHeaders
andHttpHeaders
.It is inefficient since it copies headers two times.
After walking through Netty's API, I found out it is possible
to set a custom
Http2HeadersDecoder
that converts a headers block intoHttpHeaders
when buildingHttp2ServerConnectionHandler
Modifications:
ArmeriaHttp2Headers
that implements Netty'sHttp2Headers
andinternally stores the values in Armeria's
HttpHeaders
.Http2ConnectionEncoder
andHttp2ConnectionDecoder
on both server and client side to customize
Http2HeadersDecoder
ArmeriaHttpUtil
to directly convertHttp2Headers
toHttpHeaders
without copies.Result:
com.linecorp.armeria.logging.traffic.{server,client}.http2
.For example: