Skip to content

Commit

Permalink
feat: allow server IP binding
Browse files Browse the repository at this point in the history
  • Loading branch information
qixils committed Jan 6, 2024
1 parent d0754a8 commit c72d4e1
Show file tree
Hide file tree
Showing 12 changed files with 153 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable;
import java.math.BigInteger;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
Expand Down Expand Up @@ -63,16 +64,16 @@ static String encryptPassword(@NotNull String password) {
}

/**
* Returns the IP that the {@link SocketManager} will listen on.
* If running in server mode, this will be null.
* Returns the IP that the {@link SocketManager} will listen on or bind to.
* May be null for servers to bind to all local IPs.
*
* @return IP if available
* @since 3.3.0
*/
@ApiStatus.AvailableSince("3.3.0")
@Nullable
@CheckReturnValue
String getIP();
InetAddress getIP();

/**
* Returns the port that the {@link SocketManager} will listen on.
Expand Down
19 changes: 11 additions & 8 deletions receiver/src/main/java/dev/qixils/crowdcontrol/CrowdControl.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.net.InetAddress;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
Expand Down Expand Up @@ -78,7 +79,7 @@ public final class CrowdControl implements SocketManager, RequestManager {
private final Map<String, Function<Request, Response>> effectHandlers = new HashMap<>();
private final Map<String, Consumer<Request>> asyncHandlers = new HashMap<>();
private final List<Function<Request, CheckResult>> globalChecks = new ArrayList<>();
private final @Nullable String IP;
private final @Nullable InetAddress IP;
private final int port;
private final @Nullable String password;
private final SocketManager socketManager;
Expand All @@ -94,11 +95,11 @@ public final class CrowdControl implements SocketManager, RequestManager {
* Creates a new receiver client that receives {@link Request}s from a streamer's Crowd Control
* desktop application.
*
* @param IP IP address to connect to (if applicable)
* @param IP IP address to connect to
* @param port port to listen on or connect to
* @param socketManagerCreator creator of a new {@link SocketManager}
*/
CrowdControl(@NotNull String IP,
CrowdControl(@NotNull InetAddress IP,
int port,
@NotNull Function<@NotNull CrowdControl, @NotNull SocketManager> socketManagerCreator) {
this.IP = ExceptionUtil.validateNotNull(IP, "IP");
Expand All @@ -110,14 +111,16 @@ public final class CrowdControl implements SocketManager, RequestManager {
/**
* Creates a new receiver server that receives {@link Request}s from multiple streamers.
*
* @param IP IP address to bind to (if applicable)
* @param port port to listen on or connect to
* @param password password to use to connect to the server
* @param socketManagerCreator creator of a new {@link SocketManager}
*/
CrowdControl(int port,
CrowdControl(@Nullable InetAddress IP,
int port,
@NotNull String password,
@NotNull Function<@NotNull CrowdControl, @NotNull SocketManager> socketManagerCreator) {
this.IP = null;
this.IP = IP;
this.port = port;
this.password = ServiceManager.encryptPassword(ExceptionUtil.validateNotNull(password, "password"));
this.socketManager = ExceptionUtil.validateNotNull(socketManagerCreator, "socketManagerCreator").apply(this);
Expand Down Expand Up @@ -163,16 +166,16 @@ private static void methodHandlerWarning(@NotNull Method method, @NotNull String
}

/**
* Returns the IP that the {@link SocketManager} will listen on.
* If running in server mode, this will be null.
* Returns the IP that the {@link SocketManager} will listen on or bind to.
* May be null for servers to bind to all local IPs.
*
* @return IP if available
* @since 1.0.0
*/
@ApiStatus.AvailableSince("1.0.0")
@Nullable
@CheckReturnValue
public String getIP() {
public InetAddress getIP() {
return IP;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.jetbrains.annotations.NotNull;

import javax.annotation.CheckReturnValue;
import java.net.InetAddress;

/**
* Builds a new {@link CrowdControl} instance.
Expand All @@ -13,7 +14,36 @@
*/
@ApiStatus.AvailableSince("3.0.0")
@ApiStatus.Internal
interface CrowdControlBuilder {
interface CrowdControlBuilder<B extends CrowdControlBuilder<B>> {

/**
* Sets the IP that the Crowd Control client will connect to.
*
* @param IP IP to connect to
* @return this builder
* @throws IllegalArgumentException the IP was null or blank
* @since 3.9.0
*/
@ApiStatus.AvailableSince("3.9.0")
@CheckReturnValue
@Contract("_ -> this")
@NotNull
B ip(@NotNull InetAddress IP);

/**
* Sets the IP that the Crowd Control client will connect to.
*
* @param IP IP to connect to
* @return this builder
* @throws IllegalArgumentException the IP was null or blank
* @since 3.9.0
*/
@ApiStatus.AvailableSince("3.9.0")
@CheckReturnValue
@Contract("_ -> this")
@NotNull
B ip(@NotNull String IP) throws IllegalArgumentException;

/**
* Sets the port that will be used by the Crowd Control client or server.
*
Expand All @@ -26,7 +56,7 @@ interface CrowdControlBuilder {
@CheckReturnValue
@NotNull
@Contract("_ -> this")
CrowdControlBuilder port(int port) throws IllegalArgumentException;
B port(int port) throws IllegalArgumentException;

/**
* Builds a new {@link CrowdControl} instance using the provided variables.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@
import org.jetbrains.annotations.NotNull;

import javax.annotation.CheckReturnValue;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.function.Function;

/**
* A base that implements common methods for building a new {@link CrowdControl} instance.
*
* @since 3.0.0
*/
@SuppressWarnings("unchecked")
@ApiStatus.AvailableSince("3.0.0")
@ApiStatus.Internal
abstract class CrowdControlBuilderBase implements CrowdControlBuilder {
abstract class CrowdControlBuilderBase<B extends CrowdControlBuilderBase<B>> implements CrowdControlBuilder<B> {
/**
* A function (usually a constructor) that creates a new {@link SocketManager}
* given a {@link CrowdControl} instance.
Expand All @@ -26,6 +29,14 @@ abstract class CrowdControlBuilderBase implements CrowdControlBuilder {
@ApiStatus.AvailableSince("3.0.0")
protected final @NotNull Function<@NotNull CrowdControl, @NotNull SocketManager> socketManagerCreator;

/**
* The IP that the client/server will connect to or bind on.
*
* @since 3.9.0
*/
@ApiStatus.AvailableSince("3.9.0")
protected InetAddress IP;

/**
* The port that the client/server will connect to or listen on.
*
Expand All @@ -50,11 +61,51 @@ abstract class CrowdControlBuilderBase implements CrowdControlBuilder {
@CheckReturnValue
@Contract("_ -> this")
@ApiStatus.AvailableSince("3.0.0")
public @NotNull CrowdControlBuilderBase port(int port) throws IllegalArgumentException {
public @NotNull B port(int port) throws IllegalArgumentException {
if (port < 1 || port > 65536) {
throw new IllegalArgumentException("Port should be within [1,65536]");
}
this.port = port;
return this;
return (B) this;
}

/**
* {@inheritDoc}
*
* @param IP IP to connect to
* @return this builder
* @throws IllegalArgumentException the IP was null or blank
* @since 3.9.0
*/
@ApiStatus.AvailableSince("3.9.0")
@CheckReturnValue
@Contract("_ -> this")
public @NotNull B ip(@NotNull InetAddress IP) throws IllegalArgumentException {
this.IP = ExceptionUtil.validateNotNull(IP, "IP");
return (B) this;
}

/**
* {@inheritDoc}
*
* @param IP IP to connect to
* @return this builder
* @throws IllegalArgumentException the IP was null or blank
* @since 3.9.0
*/
@ApiStatus.AvailableSince("3.9.0")
@CheckReturnValue
@Contract("_ -> this")
public @NotNull B ip(@NotNull String IP) throws IllegalArgumentException {
ExceptionUtil.validateNotNull(IP, "IP");
if (IP.isEmpty()) {
throw new IllegalArgumentException("IP cannot be blank");
}
try {
this.IP = InetAddress.getByName(IP);
} catch (UnknownHostException e) {
throw new IllegalArgumentException("Invalid IP address " + IP, e);
}
return (B) this;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package dev.qixils.crowdcontrol;

import dev.qixils.crowdcontrol.exceptions.ExceptionUtil;
import dev.qixils.crowdcontrol.socket.ClientSocketManager;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Contract;
Expand All @@ -15,9 +14,7 @@
* @since 3.0.0
*/
@ApiStatus.AvailableSince("3.0.0")
public final class CrowdControlClientBuilder extends CrowdControlBuilderBase {

private String IP;
public final class CrowdControlClientBuilder extends CrowdControlBuilderBase<CrowdControlClientBuilder> {

/**
* Creates a new {@link CrowdControl} client builder.
Expand All @@ -30,42 +27,6 @@ public CrowdControlClientBuilder() {
super(ClientSocketManager::new);
}

/**
* Sets the IP that the Crowd Control client will connect to.
*
* @param IP IP to connect to
* @return this builder
* @throws IllegalArgumentException the IP was null or blank
* @since 3.0.0
*/
@ApiStatus.AvailableSince("3.0.0")
@CheckReturnValue
@Contract("_ -> this")
public @NotNull CrowdControlClientBuilder ip(@NotNull String IP) throws IllegalArgumentException {
ExceptionUtil.validateNotNull(IP, "IP");
if (IP.isEmpty()) {
throw new IllegalArgumentException("IP cannot be blank");
}
this.IP = IP;
return this;
}

/**
* Sets the port that will be used by the Crowd Control client.
*
* @param port port to connect to
* @return this builder
* @throws IllegalArgumentException the port was outside the expected bounds of [1,65536]
* @since 3.0.0
*/
@ApiStatus.AvailableSince("3.0.0")
@Override
@CheckReturnValue
@Contract("_ -> this")
public @NotNull CrowdControlClientBuilder port(int port) throws IllegalArgumentException {
return (CrowdControlClientBuilder) super.port(port);
}

/**
* {@inheritDoc}
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* @since 3.0.0
*/
@ApiStatus.AvailableSince("3.0.0")
public final class CrowdControlServerBuilder extends CrowdControlBuilderBase {
public final class CrowdControlServerBuilder extends CrowdControlBuilderBase<CrowdControlServerBuilder> {
private String password;

/**
Expand Down Expand Up @@ -49,22 +49,6 @@ public CrowdControlServerBuilder() {
return this;
}

/**
* Sets the port that will be used by the Crowd Control server.
*
* @param port port to listen for new connections on
* @return this builder
* @throws IllegalArgumentException the port was outside the expected bounds of [1,65536]
* @since 3.0.0
*/
@ApiStatus.AvailableSince("3.0.0")
@Override
@CheckReturnValue
@Contract("_ -> this")
public @NotNull CrowdControlServerBuilder port(int port) throws IllegalArgumentException {
return (CrowdControlServerBuilder) super.port(port);
}

/**
* {@inheritDoc}
*
Expand All @@ -83,6 +67,6 @@ public CrowdControlServerBuilder() {
if (password == null) {
throw new IllegalStateException("Password must be set using #password(String)");
}
return new CrowdControl(port, password, socketManagerCreator);
return new CrowdControl(IP, port, password, socketManagerCreator);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ private void loop() {
return;

try {
serverSocket = new ServerSocket(crowdControl.getPort());
serverSocket = new ServerSocket(crowdControl.getPort(), 0, crowdControl.getIP());
} catch (IOException exc) {
logger.error("Could not register port " + crowdControl.getPort() + ". This is a fatal exception; attempts to reconnect will not be made.", exc);
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ public void constructorTest() throws InterruptedException {
CrowdControl server = CrowdControl.server().port(PORT).password("password").build();
server.shutdown();
Assertions.assertEquals(PORT, server.getPort());
//noinspection SpellCheckingInspection
Assertions.assertEquals(
"b109f3bbbc244eb82441917ed06d618b9008dd09b3befd1b5e07394c706a8bb980b1d7785e5976ec049b46df5f1326af5a2ea6d103fd07c95385ffab0cacbc86",
server.getPassword()
Expand All @@ -25,7 +24,8 @@ public void constructorTest() throws InterruptedException {
CrowdControl client = CrowdControl.client().port(PORT + 1).ip("localhost").build();
client.shutdown("test");
Assertions.assertEquals(PORT + 1, client.getPort());
Assertions.assertEquals("localhost", client.getIP());
Assertions.assertNotNull(client.getIP());
Assertions.assertEquals("localhost", client.getIP().getHostName());

Thread.sleep(20);
}
Expand Down
Loading

0 comments on commit c72d4e1

Please sign in to comment.