-
Notifications
You must be signed in to change notification settings - Fork 926
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
Add Unix domain socket support #4846
Conversation
The memory leak is very likely from |
req, new IllegalArgumentException("Invalid path: " + originalPath)); | ||
req, new IllegalArgumentException("Invalid request target: " + originalPath)); |
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.
This isn't always a path but can be an absolute URI, so I changed it to 'request target'.
void addBeforeSessionHandler(ChannelPipeline pipeline, ChannelHandler handler) { | ||
// Get the name of the HttpSessionHandler so that we can put our handlers before it. | ||
final ChannelHandlerContext lastContext = pipeline.lastContext(); | ||
assert lastContext.handler().getClass() == HttpSessionHandler.class; | ||
|
||
pipeline.addBefore(lastContext.name(), null, handler); | ||
if (lastContext.handler().getClass() == HttpSessionHandler.class) { | ||
// Get the name of the HttpSessionHandler so that we can put our handlers before it. | ||
pipeline.addBefore(lastContext.name(), null, handler); | ||
} else { | ||
// HttpSessionHandler was not added yet. | ||
pipeline.addLast(handler); | ||
} | ||
} |
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.
This one is a little bit tricky because a connect()
operation in domain sockets succeeds immediately in Linux and thus this method can be invoked even before HttpSessionHandler
is added.
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 good all in all. 👍
core/src/main/java/com/linecorp/armeria/client/HttpChannelPool.java
Outdated
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/client/HttpClientFactory.java
Outdated
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/client/HttpClientPipelineConfigurator.java
Outdated
Show resolved
Hide resolved
// Use an IPv6 address that falls into RFC 6666 (IPv6 Discard Prefix), | ||
// which will never reach anywhere. See https://datatracker.ietf.org/doc/rfc6666/ | ||
private static final byte[] IPV6_DISCARD_ADDR = { | ||
1, 0, 0, 0, 0, 0, 0, 0, // 0100::/64 |
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.
Question: Isn't it 0, 0, 0, 1, 0, 0, 0, 0
?
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.
I guess not:
jshell> var addr = InetAddress.getByAddress(new byte[] { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
addr ==> /100:0:0:0:0:0:0:0
jshell> var addr = InetAddress.getByAddress(new byte[] { 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
addr ==> /0:1:0:0:0:0:0:0
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 for the explanation. 🙇
core/src/main/java/com/linecorp/armeria/common/util/TransportType.java
Outdated
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/common/util/TransportType.java
Outdated
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/common/util/TransportType.java
Outdated
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/internal/common/ArmeriaHttpUtil.java
Outdated
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/internal/common/ArmeriaHttpUtil.java
Show resolved
Hide resolved
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.
Great! I learned a lot from this work.
core/src/test/java/com/linecorp/armeria/client/DomainSocketClientTest.java
Outdated
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/client/HttpChannelPool.java
Outdated
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/client/SessionProtocolNegotiationCache.java
Outdated
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/internal/client/DefaultClientRequestContext.java
Outdated
Show resolved
Hide resolved
core/src/main/java/com/linecorp/armeria/server/HttpServerHandler.java
Outdated
Show resolved
Hide resolved
41c1ce6
to
e65ad13
Compare
Motivation: In service mesh environment, it's quite common for a sidecar to communicate with an app using a Unix domain socket. Armeria could be used in such a scenarios if it is capable of serving requests on a Unix domain socket. Modifications: - Added `DomainSocketAddress` - Added `ServerPort.isDomainSocket()` - Added various getters to `TransportType` and `TransportTypeProvider` so it provides the transport-specific information about domain socket classes and whether the transport supports domain sockets - Added `ChannelUtil.localAddress()` and `remoteAddress()` and replace the direct invocation of `Channel.localAddress()` and `remoteAddress()` to work around the issue where Netty's domain socket channel returns `null` - See: netty/netty#13323 - Added `ChannelUtil.isTcpOption()` to determine whether the given `ChannelOption` is for TCP or not, so that a user doesn't get a WARN log when they use domain sockets - Made `ServerRule` and `ServerExtension`not instantiate their default `WebClient`s lazily, so that a user can start a serer that listens only on a domain socket Result: - Armeria is now capable of serving requests from a Unix domain socket, making it more service-mesh-friendly. Fix leaks Update Netty to 4.1.92 Cache remote/localAddress / SocketAddress -> InetSocketAddress Add client-side domain socket support Lint Windows Formatting Windows / Lint Review comments Remove an unused field
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, @trustin! 💯 👍
} | ||
['osx-x86_64', 'osx-aarch_64'].each { arch -> | ||
implementation(variantOf(libs.netty.transport.native.unix.common) { classifier(arch) }) | ||
implementation(variantOf(libs.netty.transport.native.kqueue) { classifier(arch) }) |
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.
🙇♂️
core/src/main/java/com/linecorp/armeria/client/HttpChannelPool.java
Outdated
Show resolved
Hide resolved
core/src/test/java/com/linecorp/armeria/client/SessionProtocolNegotiationCacheTest.java
Outdated
Show resolved
Hide resolved
… the comments from @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.
Awesome feature! 🚀 💡
I see the following test failures on my Mac:
Do you get the same failures, @ikhoon? They look irrelevant to this pull request 🤔 |
This should fix them: #4903 |
I also see some flaky tests, which may or may not be related to this PR:
|
…tPort(SessionProtocol)`
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 a minor suggestion but looks good to me 👍 Thanks @trustin ! 🙇 👍 🙇
core/src/main/java/com/linecorp/armeria/client/HttpChannelPool.java
Outdated
Show resolved
Hide resolved
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.
Still LGTM. 👍
Co-authored-by: minux <songmw725@gmail.com>
Co-authored-by: Ikhun Um <ih.pert@gmail.com>
Co-authored-by: Ikhun Um <ih.pert@gmail.com>
Co-authored-by: Ikhun Um <ih.pert@gmail.com>
….java Co-authored-by: jrhee17 <guins_j@guins.org>
Addressed all comments 😆 |
Looks good with the new changes. |
Thanks for reviewing this large PR, folks! 🙇 |
Motivation: In service mesh environment, it's quite common for a sidecar to communicate with an app using a Unix domain socket. Armeria could be used more efficiently than using TCP/IP in such a scenarios with domain socket support. Modifications: - Updated the various places to support domain sockets, as listed below - Common changes: - Added `DomainSocketAddress` - `DomainSocketAddress` is a subtype of `InetSocketAddress` for backward compatibility with the existing API that returns `InetSocketAddress` rather than `SocketAddress`. - (Breaking) Changed the return type of `RequestContext.localAddress()` and `remoteAddress()` from `SocketAddress` and `InetSocketAddress` because `DomainSocketAddress` is now an `InetSocketAddress`. - (Breaking) The parameter type of the following methods has been changed from `SocketAddress` to `InetSocketAddress`: - `ClientRequestContextBuilder.localAddress()` - `ClientRequestContextBuilder.remoteAddress()` - `ServiceRequestContextBuilder.localAddress()` - `ClientRequestContextBuilder.remoteAddress()` - Added various getters to `TransportType` and `TransportTypeProvider` so it provides the transport-specific information about domain socket classes and whether the transport supports domain sockets - Added `ChannelUtil.localAddress()` and `remoteAddress()` and replace the direct invocation of `Channel.localAddress()` and `remoteAddress()` to convert Netty's `DomainSocketAddress` into ours. - Updated Netty to 4.1.92 that contains the fix for the problem where `DomainSocketChannel` returns `null` addresses. - Added `ChannelUtil.isTcpOption()` to determine whether the given `ChannelOption` is for TCP or not, so that a user doesn't get a WARN log when they use domain sockets - Server-side changes: - Added `ServerPort.isDomainSocket()` - Updated `Server` and `HttpServerHandler` to support domain sockets. - Made `ServerRule` and `ServerExtension` not instantiate their default `WebClient`s lazily, so that a user can start a serer that listens only on a domain socket - Client-side changes: - Added domain socket address support to `Endpoint`: - Made `Endpoint` accept a domain socket address in its `host` field - Added `Endpoint.of(SocketAddress)` so that a user can easily create an `Endpoint` from an `InetSocketAddress` or a `DomainSocketAddress` - Added `Endpoint.isDomainSocket()` - Updated `HttpClientFactory`, `HttpChannelPool`, `HttpClientDelegate`, `HttpClientPipelineConfigurator` and `SessionProtocolNegotiationCache` to support domain sockets Result: - Armeria is now capable of serving/sending requests from/to a Unix domain socket, making it more service-mesh-friendly. - (Breaking) Changed the return type of `RequestContext.localAddress()` and `remoteAddress()` from `SocketAddress` and `InetSocketAddress` because `DomainSocketAddress` is now an `InetSocketAddress`. - (Breaking) The parameter type of the following methods has been changed from `SocketAddress` to `InetSocketAddress`: - `ClientRequestContextBuilder.localAddress()` - `ClientRequestContextBuilder.remoteAddress()` - `ServiceRequestContextBuilder.localAddress()` - `ClientRequestContextBuilder.remoteAddress()` --------- Co-authored-by: jrhee17 <guins_j@guins.org> Co-authored-by: Ikhun Um <ih.pert@gmail.com> Co-authored-by: minux <songmw725@gmail.com>
Motivation:
In service mesh environment, it's quite common for a sidecar to
communicate with an app using a Unix domain socket. Armeria could be
used more efficiently than using TCP/IP in such a scenarios with
domain socket support.
Modifications:
below
DomainSocketAddress
DomainSocketAddress
is a subtype ofInetSocketAddress
forbackward compatibility with the existing API that returns
InetSocketAddress
rather thanSocketAddress
.RequestContext.localAddress()
and
remoteAddress()
fromSocketAddress
andInetSocketAddress
because
DomainSocketAddress
is now anInetSocketAddress
.changed from
SocketAddress
toInetSocketAddress
:ClientRequestContextBuilder.localAddress()
ClientRequestContextBuilder.remoteAddress()
ServiceRequestContextBuilder.localAddress()
ClientRequestContextBuilder.remoteAddress()
TransportType
andTransportTypeProvider
so it provides the transport-specific information about domain socket
classes and whether the transport supports domain sockets
ChannelUtil.localAddress()
andremoteAddress()
and replacethe direct invocation of
Channel.localAddress()
andremoteAddress()
to convert Netty'sDomainSocketAddress
into ours.DomainSocketChannel
returnsnull
addresses.ChannelUtil.isTcpOption()
to determine whether the givenChannelOption
is for TCP or not, so that a user doesn't get a WARNlog when they use domain sockets
ServerPort.isDomainSocket()
Server
andHttpServerHandler
to support domain sockets.ServerRule
andServerExtension
not instantiate their defaultWebClient
s lazily, so that a user can start a serer that listensonly on a domain socket
Endpoint
:Endpoint
accept a domain socket address in itshost
fieldEndpoint.of(SocketAddress)
so that a user can easily createan
Endpoint
from anInetSocketAddress
or aDomainSocketAddress
Endpoint.isDomainSocket()
HttpClientFactory
,HttpChannelPool
,HttpClientDelegate
,HttpClientPipelineConfigurator
andSessionProtocolNegotiationCache
to support domain sockets
Result:
socket, making it more service-mesh-friendly.
RequestContext.localAddress()
and
remoteAddress()
fromSocketAddress
andInetSocketAddress
because
DomainSocketAddress
is now anInetSocketAddress
.changed from
SocketAddress
toInetSocketAddress
:ClientRequestContextBuilder.localAddress()
ClientRequestContextBuilder.remoteAddress()
ServiceRequestContextBuilder.localAddress()
ClientRequestContextBuilder.remoteAddress()