forked from micronaut-projects/micronaut-core
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support unix domain sockets for the access log handler (micronaut-pro…
…jects#10940) * Support unix domain sockets for the access log handler Fixes LMP-319 * fix checkstyle
- Loading branch information
Showing
14 changed files
with
425 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
129 changes: 129 additions & 0 deletions
129
...ain/java/io/micronaut/http/server/netty/handler/accesslog/element/ConnectionMetadata.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
/* | ||
* Copyright 2017-2024 original authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package io.micronaut.http.server.netty.handler.accesslog.element; | ||
|
||
import io.micronaut.core.annotation.NonNull; | ||
import io.netty.channel.Channel; | ||
import io.netty.channel.socket.DatagramChannel; | ||
import io.netty.channel.socket.SocketChannel; | ||
import io.netty.channel.socket.nio.NioDomainSocketChannel; | ||
|
||
import java.net.InetSocketAddress; | ||
import java.net.SocketAddress; | ||
import java.net.UnixDomainSocketAddress; | ||
import java.util.Optional; | ||
|
||
/** | ||
* Connection-level metadata for logging e.g. addresses or ports. | ||
* | ||
* @since 4.6.0 | ||
* @author Jonas Konrad | ||
*/ | ||
public interface ConnectionMetadata { | ||
/** | ||
* The local address of this connection, if applicable. | ||
* | ||
* @return The local address | ||
*/ | ||
@NonNull | ||
Optional<SocketAddress> localAddress(); | ||
|
||
/** | ||
* The remote address of this connection, if applicable. | ||
* | ||
* @return The remote address | ||
*/ | ||
@NonNull | ||
Optional<SocketAddress> remoteAddress(); | ||
|
||
/** | ||
* Get the host address string of the given {@link SocketAddress} instance. This is usually the | ||
* numeric IP, but for unix domain socket it is the file path. | ||
* | ||
* @param a The address | ||
* @return The string representation, or {@link Optional#empty()} if this type of address is | ||
* not supported | ||
*/ | ||
static Optional<String> getHostAddress(@NonNull SocketAddress a) { | ||
if (a instanceof InetSocketAddress addr) { | ||
return Optional.of(addr.getAddress().getHostAddress()); | ||
} else { | ||
return getHostName(a); | ||
} | ||
} | ||
|
||
/** | ||
* Get the host name of the given {@link SocketAddress} instance. This is usually the DNS name | ||
* or IP, but for unix domain socket it is the file path. | ||
* | ||
* @param a The address | ||
* @return The string representation, or {@link Optional#empty()} if this type of address is | ||
* not supported | ||
*/ | ||
static Optional<String> getHostName(@NonNull SocketAddress a) { | ||
if (a instanceof InetSocketAddress addr) { | ||
return Optional.of(addr.getAddress().getHostName()); | ||
} else if (a instanceof UnixDomainSocketAddress addr) { | ||
return Optional.of(addr.getPath().toString()) | ||
// remote address is empty string | ||
.filter(s -> !s.isEmpty()); | ||
} else if (ConnectionMetadataImpl.DOMAIN_SOCKET_ADDRESS.isInstance(a)) { | ||
String path = ConnectionMetadataImpl.DomainSocketUtil.getPath(a); | ||
if (path.isEmpty() || path.equals("\0")) { | ||
return Optional.empty(); | ||
} | ||
if (path.startsWith("\0")) { | ||
// *abstract* unix domain sockets start with a NUL byte | ||
path = "@" + path.substring(1); | ||
} | ||
return Optional.of(path); | ||
} else { | ||
return Optional.empty(); | ||
} | ||
} | ||
|
||
/** | ||
* Create a new {@link ConnectionMetadata} instance for the given netty channel. | ||
* | ||
* @param channel The channel | ||
* @return The metadata, potentially {@link #empty()} | ||
*/ | ||
@NonNull | ||
static ConnectionMetadata ofNettyChannel(@NonNull Channel channel) { | ||
if (channel instanceof SocketChannel sc) { | ||
return new ConnectionMetadataImpl.SocketChannelMetadata(sc); | ||
} else if (ConnectionMetadataImpl.QUIC_CHANNEL != null && ConnectionMetadataImpl.QUIC_CHANNEL.isInstance(channel)) { | ||
return new ConnectionMetadataImpl.QuicChannelMetadata(channel); | ||
} else if (channel instanceof DatagramChannel || channel instanceof NioDomainSocketChannel || (ConnectionMetadataImpl.DOMAIN_SOCKET_CHANNEL != null && ConnectionMetadataImpl.DOMAIN_SOCKET_CHANNEL.isInstance(channel))) { | ||
return new ConnectionMetadataImpl.GenericChannelMetadata(channel); | ||
} else if (channel.parent() != null) { | ||
// QUIC / HTTP/2 stream channels | ||
return ofNettyChannel(channel.parent()); | ||
} else { | ||
return empty(); | ||
} | ||
} | ||
|
||
/** | ||
* Placeholder metadata for unsupported channel types. | ||
* | ||
* @return An empty metadata instance | ||
*/ | ||
@NonNull | ||
static ConnectionMetadata empty() { | ||
return ConnectionMetadataImpl.Empty.INSTANCE; | ||
} | ||
} |
128 changes: 128 additions & 0 deletions
128
...java/io/micronaut/http/server/netty/handler/accesslog/element/ConnectionMetadataImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
/* | ||
* Copyright 2017-2024 original authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* https://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package io.micronaut.http.server.netty.handler.accesslog.element; | ||
|
||
import io.micronaut.core.annotation.Internal; | ||
import io.micronaut.core.annotation.NonNull; | ||
import io.netty.channel.Channel; | ||
import io.netty.channel.socket.SocketChannel; | ||
import io.netty.channel.unix.DomainSocketAddress; | ||
import io.netty.channel.unix.DomainSocketChannel; | ||
import io.netty.incubator.codec.quic.QuicChannel; | ||
|
||
import java.net.SocketAddress; | ||
import java.util.Optional; | ||
|
||
/** | ||
* Implementations of {@link ConnectionMetadata}. | ||
* | ||
* @since 4.6.0 | ||
* @author Jonas Konrad | ||
*/ | ||
@Internal | ||
final class ConnectionMetadataImpl { | ||
static final Class<?> QUIC_CHANNEL; | ||
static final Class<?> DOMAIN_SOCKET_ADDRESS; | ||
static final Class<?> DOMAIN_SOCKET_CHANNEL; | ||
|
||
static { | ||
Class<QuicChannel> quicChannelClass; | ||
try { | ||
quicChannelClass = QuicChannel.class; | ||
} catch (Exception e) { | ||
quicChannelClass = null; | ||
} | ||
QUIC_CHANNEL = quicChannelClass; | ||
|
||
Class<DomainSocketAddress> domainSocketAddressClass; | ||
Class<DomainSocketChannel> domainSocketChannelClass; | ||
try { | ||
domainSocketAddressClass = DomainSocketAddress.class; | ||
domainSocketChannelClass = DomainSocketChannel.class; | ||
} catch (Exception e) { | ||
domainSocketAddressClass = null; | ||
domainSocketChannelClass = null; | ||
} | ||
DOMAIN_SOCKET_ADDRESS = domainSocketAddressClass; | ||
DOMAIN_SOCKET_CHANNEL = domainSocketChannelClass; | ||
} | ||
|
||
static class DomainSocketUtil { | ||
static String getPath(SocketAddress address) { | ||
return ((DomainSocketAddress) address).path(); | ||
} | ||
} | ||
|
||
/** | ||
* This one is separate from {@link GenericChannelMetadata} because it has special handling for | ||
* compatibility. | ||
* | ||
* @param ch The channel | ||
*/ | ||
@Internal | ||
record SocketChannelMetadata(SocketChannel ch) implements ConnectionMetadata { | ||
@Override | ||
public @NonNull Optional<SocketAddress> localAddress() { | ||
return Optional.of(ch.localAddress()); | ||
} | ||
|
||
@Override | ||
public @NonNull Optional<SocketAddress> remoteAddress() { | ||
return Optional.of(ch.remoteAddress()); | ||
} | ||
} | ||
|
||
@Internal | ||
record GenericChannelMetadata(Channel ch) implements ConnectionMetadata { | ||
@Override | ||
public @NonNull Optional<SocketAddress> localAddress() { | ||
return Optional.of(ch.localAddress()); | ||
} | ||
|
||
@Override | ||
public @NonNull Optional<SocketAddress> remoteAddress() { | ||
return Optional.of(ch.remoteAddress()); | ||
} | ||
} | ||
|
||
@Internal | ||
record QuicChannelMetadata(Channel ch) implements ConnectionMetadata { | ||
@Override | ||
public @NonNull Optional<SocketAddress> localAddress() { | ||
return Optional.ofNullable(((QuicChannel) ch).localSocketAddress()); | ||
} | ||
|
||
@Override | ||
public @NonNull Optional<SocketAddress> remoteAddress() { | ||
return Optional.ofNullable(((QuicChannel) ch).remoteSocketAddress()); | ||
} | ||
} | ||
|
||
@Internal | ||
public static final class Empty implements ConnectionMetadata { | ||
static final ConnectionMetadata INSTANCE = new Empty(); | ||
|
||
@Override | ||
public @NonNull Optional<SocketAddress> localAddress() { | ||
return Optional.empty(); | ||
} | ||
|
||
@Override | ||
public @NonNull Optional<SocketAddress> remoteAddress() { | ||
return Optional.empty(); | ||
} | ||
} | ||
} |
Oops, something went wrong.