io.kroxylicious.proxy.internal.ProxyChannelState Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kroxylicious-runtime Show documentation
Show all versions of kroxylicious-runtime Show documentation
The proxy code which provides the runtime environment in which filters execute
The newest version!
/*
* Copyright Kroxylicious Authors.
*
* Licensed under the Apache Software License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package io.kroxylicious.proxy.internal;
import java.util.List;
import java.util.Objects;
import org.apache.kafka.common.message.ApiVersionsRequestData;
import io.netty.handler.codec.haproxy.HAProxyMessage;
import io.kroxylicious.proxy.filter.NetFilter;
import io.kroxylicious.proxy.frame.DecodedRequestFrame;
import io.kroxylicious.proxy.service.HostPort;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import static io.kroxylicious.proxy.internal.ProxyChannelState.ApiVersions;
import static io.kroxylicious.proxy.internal.ProxyChannelState.ClientActive;
import static io.kroxylicious.proxy.internal.ProxyChannelState.Closed;
import static io.kroxylicious.proxy.internal.ProxyChannelState.Connecting;
import static io.kroxylicious.proxy.internal.ProxyChannelState.Forwarding;
import static io.kroxylicious.proxy.internal.ProxyChannelState.HaProxy;
import static io.kroxylicious.proxy.internal.ProxyChannelState.SelectingServer;
import static io.kroxylicious.proxy.internal.ProxyChannelState.Startup;
/**
* Root of a sealed class hierarchy representing the states of the {@link ProxyChannelStateMachine}.
*/
sealed interface ProxyChannelState permits
Startup,
ClientActive,
HaProxy,
ApiVersions,
SelectingServer,
Connecting,
Forwarding,
Closed {
/**
* The statemachine has just been created.
*/
record Startup() implements ProxyChannelState {
public static final Startup STARTING_STATE = new Startup();
public ClientActive toClientActive() {
return new ClientActive();
}
}
/**
* The initial state, when a client has connected, but no messages
* have been received yet.
*/
record ClientActive() implements ProxyChannelState {
/**
* Transition to {@link HaProxy}, because a PROXY header has been received
* @return The HaProxy state
*/
public @NonNull HaProxy toHaProxy(HAProxyMessage haProxyMessage) {
return new HaProxy(haProxyMessage);
}
/**
* Transition to {@link ApiVersions}, because an ApiVersions request has been received
* @return The ApiVersions state
*/
@NonNull
public ApiVersions toApiVersions(
DecodedRequestFrame apiVersionsFrame) {
// TODO check the format of the strings using a regex
// Needed to reproduce the exact behaviour for how a broker handles this
// see org.apache.kafka.common.requests.ApiVersionsRequest#isValid()
var clientSoftwareName = apiVersionsFrame.body().clientSoftwareName();
var clientSoftwareVersion = apiVersionsFrame.body().clientSoftwareVersion();
return new ApiVersions(
null,
clientSoftwareName,
clientSoftwareVersion);
}
/**
* Transition to {@link SelectingServer}, because some non-ApiVersions request has been received
* @return The Connecting state
*/
@NonNull
public SelectingServer toSelectingServer(@Nullable DecodedRequestFrame apiVersionsFrame) {
return new SelectingServer(
null,
apiVersionsFrame == null ? null : apiVersionsFrame.body().clientSoftwareName(),
apiVersionsFrame == null ? null : apiVersionsFrame.body().clientSoftwareVersion());
}
}
/**
* A PROXY protocol header has been received on the channel
* @param haProxyMessage The information in the PROXY header
*/
record HaProxy(
@NonNull HAProxyMessage haProxyMessage)
implements ProxyChannelState {
/**
* Transition to {@link ApiVersions}, because an ApiVersions request has been received
* @return The ApiVersions state
*/
@NonNull
public ApiVersions toApiVersions(DecodedRequestFrame apiVersionsFrame) {
// TODO check the format of the strings using a regex
// Needed to reproduce the exact behaviour for how a broker handles this
// see org.apache.kafka.common.requests.ApiVersionsRequest#isValid()
var clientSoftwareName = apiVersionsFrame.body().clientSoftwareName();
var clientSoftwareVersion = apiVersionsFrame.body().clientSoftwareVersion();
return new ApiVersions(
haProxyMessage,
clientSoftwareName,
clientSoftwareVersion);
}
/**
* Transition to {@link SelectingServer}, because some non-ApiVersions request has been received
* @return The Connecting state
*/
@NonNull
public SelectingServer toSelectingServer(@Nullable DecodedRequestFrame apiVersionsFrame) {
return new SelectingServer(
haProxyMessage,
apiVersionsFrame == null ? null : apiVersionsFrame.body().clientSoftwareName(),
apiVersionsFrame == null ? null : apiVersionsFrame.body().clientSoftwareVersion());
}
}
/**
* The client has sent an ApiVersions request
* @param haProxyMessage
* @param clientSoftwareName
* @param clientSoftwareVersion
*/
record ApiVersions(@Nullable HAProxyMessage haProxyMessage,
@Nullable String clientSoftwareName, // optional in the protocol
@Nullable String clientSoftwareVersion // optional in the protocol
) implements ProxyChannelState {
/**
* Transition to {@link SelectingServer}, because some non-ApiVersions request has been received
* @return The Connecting state
*/
@NonNull
public SelectingServer toSelectingServer() {
return new SelectingServer(
haProxyMessage,
clientSoftwareName,
clientSoftwareVersion);
}
}
/**
* A channel to the server is now required, but
* {@link io.kroxylicious.proxy.filter.NetFilter#selectServer(NetFilter.NetFilterContext)}
* has not yet been called.
* @param haProxyMessage
* @param clientSoftwareName
* @param clientSoftwareVersion
*/
record SelectingServer(@Nullable HAProxyMessage haProxyMessage,
@Nullable String clientSoftwareName,
@Nullable String clientSoftwareVersion)
implements ProxyChannelState {
/**
* Transition to {@link Connecting}, because the NetFilter
* has invoked
* {@link io.kroxylicious.proxy.filter.NetFilter.NetFilterContext#initiateConnect(HostPort, List)}.
* @return The Connecting2 state
*/
public Connecting toConnecting(@NonNull HostPort remote) {
return new Connecting(haProxyMessage, clientSoftwareName,
clientSoftwareVersion, remote);
}
}
/**
* The NetFilter has determined the server to connect to,
* but the channel to it is not yet active.
*
* @param haProxyMessage
* @param clientSoftwareName
* @param clientSoftwareVersion
* @param remote
*/
record Connecting(@Nullable HAProxyMessage haProxyMessage,
@Nullable String clientSoftwareName,
@Nullable String clientSoftwareVersion,
@NonNull HostPort remote)
implements ProxyChannelState {
/**
* Transition to {@link Forwarding}
* @return The Forwarding state
*/
@NonNull
public Forwarding toForwarding() {
return new Forwarding(
haProxyMessage,
clientSoftwareName,
clientSoftwareVersion);
}
}
/**
* There's a KRPC-capable channel to the server
*/
final class Forwarding
implements ProxyChannelState {
@Nullable
private final HAProxyMessage haProxyMessage;
@Nullable
private final String clientSoftwareName;
@Nullable
private final String clientSoftwareVersion;
Forwarding(
@Nullable HAProxyMessage haProxyMessage,
@Nullable String clientSoftwareName,
@Nullable String clientSoftwareVersion) {
this.haProxyMessage = haProxyMessage;
this.clientSoftwareName = clientSoftwareName;
this.clientSoftwareVersion = clientSoftwareVersion;
}
@Nullable
public HAProxyMessage haProxyMessage() {
return haProxyMessage;
}
@Nullable
public String clientSoftwareName() {
return clientSoftwareName;
}
@Nullable
public String clientSoftwareVersion() {
return clientSoftwareVersion;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj == null || obj.getClass() != this.getClass()) {
return false;
}
var that = (Forwarding) obj;
return Objects.equals(this.haProxyMessage, that.haProxyMessage) &&
Objects.equals(this.clientSoftwareName, that.clientSoftwareName) &&
Objects.equals(this.clientSoftwareVersion, that.clientSoftwareVersion);
}
@Override
public int hashCode() {
return Objects.hash(haProxyMessage, clientSoftwareName, clientSoftwareVersion);
}
@Override
public String toString() {
return "Forwarding[" +
"haProxyMessage=" + haProxyMessage + ", " +
"clientSoftwareName=" + clientSoftwareName + ", " +
"clientSoftwareVersion=" + clientSoftwareVersion + ']';
}
}
/**
* The final state, where there are no connections to either client or server
*/
record Closed() implements ProxyChannelState {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy