|
27 | 27 |
|
28 | 28 | import io.netty.bootstrap.Bootstrap;
|
29 | 29 | import io.netty.buffer.ByteBufAllocator;
|
30 |
| -import io.netty.channel.ChannelHandlerContext; |
31 |
| -import io.netty.channel.ChannelInboundHandlerAdapter; |
32 |
| -import io.netty.channel.ChannelInitializer; |
| 30 | +import io.netty.channel.Channel; |
| 31 | +import io.netty.channel.ChannelFactory; |
| 32 | +import io.netty.channel.ChannelHandler; |
33 | 33 | import io.netty.channel.ChannelOption;
|
34 |
| -import io.netty.channel.ChannelPipeline; |
35 |
| -import io.netty.channel.DefaultEventLoopGroup; |
| 34 | +import io.netty.channel.ReflectiveChannelFactory; |
36 | 35 | import io.netty.channel.unix.PreferredDirectByteBufAllocator;
|
37 |
| -import io.netty.handler.codec.haproxy.HAProxyCommand; |
38 |
| -import io.netty.handler.codec.haproxy.HAProxyMessage; |
39 |
| -import io.netty.handler.codec.haproxy.HAProxyMessageEncoder; |
40 |
| -import io.netty.handler.codec.haproxy.HAProxyProtocolVersion; |
41 |
| -import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol; |
42 |
| -import io.netty.handler.timeout.ReadTimeoutHandler; |
43 |
| -import io.netty.handler.timeout.WriteTimeoutHandler; |
44 |
| -import io.netty.util.concurrent.DefaultThreadFactory; |
45 | 36 | import org.checkerframework.checker.nullness.qual.NonNull;
|
46 |
| -import org.geysermc.mcprotocollib.network.BuiltinFlags; |
47 |
| -import org.geysermc.mcprotocollib.network.codec.PacketCodecHelper; |
| 37 | +import org.geysermc.mcprotocollib.network.helper.NettyHelper; |
| 38 | +import org.geysermc.mcprotocollib.network.netty.MinecraftChannelInitializer; |
48 | 39 | import org.geysermc.mcprotocollib.network.packet.PacketProtocol;
|
49 |
| -import org.geysermc.mcprotocollib.network.tcp.FlushHandler; |
50 |
| -import org.geysermc.mcprotocollib.network.tcp.TcpFlowControlHandler; |
51 |
| -import org.geysermc.mcprotocollib.network.tcp.TcpPacketCodec; |
52 |
| -import org.geysermc.mcprotocollib.network.tcp.TcpPacketCompression; |
53 |
| -import org.geysermc.mcprotocollib.network.tcp.TcpPacketEncryptor; |
54 |
| -import org.geysermc.mcprotocollib.network.tcp.TcpPacketSizer; |
55 |
| -import org.geysermc.mcprotocollib.network.tcp.TcpSession; |
56 |
| -import org.geysermc.mcprotocollib.protocol.codec.MinecraftCodecHelper; |
| 40 | +import org.geysermc.mcprotocollib.network.session.ClientNetworkSession; |
57 | 41 |
|
58 |
| -import java.net.Inet4Address; |
59 | 42 | import java.net.InetSocketAddress;
|
60 | 43 | import java.net.SocketAddress;
|
61 |
| -import java.util.concurrent.CompletableFuture; |
62 | 44 | import java.util.concurrent.Executor;
|
63 |
| -import java.util.concurrent.TimeUnit; |
64 | 45 |
|
65 | 46 | /**
|
66 | 47 | * Manages a Minecraft Java session over our LocalChannel implementations.
|
67 | 48 | */
|
68 |
| -public final class LocalSession extends TcpSession { |
69 |
| - private static DefaultEventLoopGroup DEFAULT_EVENT_LOOP_GROUP; |
| 49 | +public final class LocalSession extends ClientNetworkSession { |
70 | 50 | private static PreferredDirectByteBufAllocator PREFERRED_DIRECT_BYTE_BUF_ALLOCATOR = null;
|
71 | 51 |
|
72 |
| - private final SocketAddress targetAddress; |
73 |
| - private final String clientIp; |
74 |
| - private final PacketCodecHelper codecHelper; |
| 52 | + private final SocketAddress spoofedRemoteAddress; |
75 | 53 |
|
76 |
| - public LocalSession(String host, int port, SocketAddress targetAddress, String clientIp, PacketProtocol protocol, Executor packetHandlerExecutor) { |
77 |
| - super(host, port, protocol, packetHandlerExecutor); |
78 |
| - this.targetAddress = targetAddress; |
79 |
| - this.clientIp = clientIp; |
80 |
| - this.codecHelper = protocol.createHelper(); |
| 54 | + public LocalSession(SocketAddress targetAddress, String clientIp, PacketProtocol protocol, Executor packetHandlerExecutor) { |
| 55 | + super(targetAddress, protocol, packetHandlerExecutor, null, null); |
| 56 | + this.spoofedRemoteAddress = new InetSocketAddress(clientIp, 0); |
81 | 57 | }
|
82 | 58 |
|
83 | 59 | @Override
|
84 |
| - public void connect(boolean wait, boolean transferring) { |
85 |
| - if (this.disconnected) { |
86 |
| - throw new IllegalStateException("Connection has already been disconnected."); |
87 |
| - } |
88 |
| - |
89 |
| - if (DEFAULT_EVENT_LOOP_GROUP == null) { |
90 |
| - DEFAULT_EVENT_LOOP_GROUP = new DefaultEventLoopGroup(new DefaultThreadFactory(this.getClass(), true)); |
91 |
| - Runtime.getRuntime().addShutdownHook(new Thread( |
92 |
| - () -> DEFAULT_EVENT_LOOP_GROUP.shutdownGracefully(100, 500, TimeUnit.MILLISECONDS))); |
93 |
| - } |
94 |
| - |
95 |
| - final Bootstrap bootstrap = new Bootstrap(); |
96 |
| - bootstrap.channel(LocalChannelWithRemoteAddress.class); |
97 |
| - bootstrap.handler(new ChannelInitializer<LocalChannelWithRemoteAddress>() { |
98 |
| - @Override |
99 |
| - public void initChannel(@NonNull LocalChannelWithRemoteAddress channel) { |
100 |
| - channel.spoofedRemoteAddress(new InetSocketAddress(clientIp, 0)); |
101 |
| - PacketProtocol protocol = getPacketProtocol(); |
102 |
| - protocol.newClientSession(LocalSession.this, transferring); |
103 |
| - |
104 |
| - ChannelPipeline pipeline = channel.pipeline(); |
105 |
| - |
106 |
| - addHAProxySupport(pipeline); |
107 |
| - |
108 |
| - pipeline.addLast("read-timeout", new ReadTimeoutHandler(getFlag(BuiltinFlags.READ_TIMEOUT, 30))); |
109 |
| - pipeline.addLast("write-timeout", new WriteTimeoutHandler(getFlag(BuiltinFlags.WRITE_TIMEOUT, 0))); |
110 |
| - |
111 |
| - pipeline.addLast("encryption", new TcpPacketEncryptor()); |
112 |
| - pipeline.addLast("sizer", new TcpPacketSizer(protocol.getPacketHeader(), getCodecHelper())); |
113 |
| - pipeline.addLast("compression", new TcpPacketCompression(getCodecHelper())); |
114 |
| - |
115 |
| - pipeline.addLast("flow-control", new TcpFlowControlHandler()); |
116 |
| - pipeline.addLast("codec", new TcpPacketCodec(LocalSession.this, true)); |
117 |
| - pipeline.addLast("flush-handler", new FlushHandler()); |
118 |
| - pipeline.addLast("manager", LocalSession.this); |
119 |
| - } |
120 |
| - }).group(DEFAULT_EVENT_LOOP_GROUP).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, getFlag(BuiltinFlags.CLIENT_CONNECT_TIMEOUT, 30) * 1000); |
| 60 | + protected ChannelFactory<? extends Channel> getChannelFactory() { |
| 61 | + return new ReflectiveChannelFactory<>(LocalChannelWithRemoteAddress.class); |
| 62 | + } |
121 | 63 |
|
| 64 | + @Override |
| 65 | + protected void setOptions(Bootstrap bootstrap) { |
122 | 66 | if (PREFERRED_DIRECT_BYTE_BUF_ALLOCATOR != null) {
|
123 | 67 | bootstrap.option(ChannelOption.ALLOCATOR, PREFERRED_DIRECT_BYTE_BUF_ALLOCATOR);
|
124 | 68 | }
|
| 69 | + } |
125 | 70 |
|
126 |
| - bootstrap.remoteAddress(targetAddress); |
127 |
| - |
128 |
| - CompletableFuture<Void> handleFuture = new CompletableFuture<>(); |
129 |
| - bootstrap.connect().addListener((futureListener) -> { |
130 |
| - if (!futureListener.isSuccess()) { |
131 |
| - exceptionCaught(null, futureListener.cause()); |
132 |
| - } |
| 71 | + @Override |
| 72 | + protected ChannelHandler getChannelHandler() { |
| 73 | + return new MinecraftChannelInitializer<>(channel -> { |
| 74 | + PacketProtocol protocol = getPacketProtocol(); |
| 75 | + protocol.newClientSession(LocalSession.this); |
133 | 76 |
|
134 |
| - handleFuture.complete(null); |
135 |
| - }); |
| 77 | + return LocalSession.this; |
| 78 | + }, true) { |
| 79 | + @Override |
| 80 | + public void initChannel(@NonNull Channel channel) throws Exception { |
| 81 | + ((LocalChannelWithRemoteAddress) channel).spoofedRemoteAddress(spoofedRemoteAddress); |
136 | 82 |
|
137 |
| - if (wait) { |
138 |
| - handleFuture.join(); |
139 |
| - } |
140 |
| - } |
| 83 | + NettyHelper.initializeHAProxySupport(LocalSession.this, channel); |
141 | 84 |
|
142 |
| - @Override |
143 |
| - public MinecraftCodecHelper getCodecHelper() { |
144 |
| - return (MinecraftCodecHelper) this.codecHelper; |
| 85 | + super.initChannel(channel); |
| 86 | + } |
| 87 | + }; |
145 | 88 | }
|
146 | 89 |
|
147 |
| - // TODO duplicate code |
148 |
| - private void addHAProxySupport(ChannelPipeline pipeline) { |
149 |
| - InetSocketAddress clientAddress = getFlag(BuiltinFlags.CLIENT_PROXIED_ADDRESS); |
150 |
| - if (clientAddress != null) { |
151 |
| - pipeline.addFirst("proxy-protocol-packet-sender", new ChannelInboundHandlerAdapter() { |
152 |
| - @Override |
153 |
| - public void channelActive(@NonNull ChannelHandlerContext ctx) throws Exception { |
154 |
| - HAProxyProxiedProtocol proxiedProtocol = clientAddress.getAddress() instanceof Inet4Address ? HAProxyProxiedProtocol.TCP4 : HAProxyProxiedProtocol.TCP6; |
155 |
| - InetSocketAddress remoteAddress; |
156 |
| - if (ctx.channel().remoteAddress() instanceof InetSocketAddress) { |
157 |
| - remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress(); |
158 |
| - } else { |
159 |
| - remoteAddress = new InetSocketAddress(host, port); |
160 |
| - } |
161 |
| - ctx.channel().writeAndFlush(new HAProxyMessage( |
162 |
| - HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, proxiedProtocol, |
163 |
| - clientAddress.getAddress().getHostAddress(), remoteAddress.getAddress().getHostAddress(), |
164 |
| - clientAddress.getPort(), remoteAddress.getPort() |
165 |
| - )); |
166 |
| - ctx.pipeline().remove(this); |
167 |
| - ctx.pipeline().remove("proxy-protocol-encoder"); |
168 |
| - super.channelActive(ctx); |
169 |
| - } |
170 |
| - }); |
171 |
| - pipeline.addFirst("proxy-protocol-encoder", HAProxyMessageEncoder.INSTANCE); |
172 |
| - } |
173 |
| - } |
174 |
| - |
175 | 90 | /**
|
176 | 91 | * Should only be called when direct ByteBufs should be preferred. At this moment, this should only be called on BungeeCord.
|
177 | 92 | */
|
|
0 commit comments