Tests for IceServerProperties. Integrate new attribute to Connection and generation token logic

pull/698/head
cruizba 2022-02-08 20:04:51 +01:00
parent 3274db8a61
commit fca9c7b2ab
9 changed files with 417 additions and 76 deletions

View File

@ -86,6 +86,11 @@
<version>${version.junit}</version> <version>${version.junit}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>${version.commons-validator}</version>
</dependency>
</dependencies> </dependencies>
<profiles> <profiles>

View File

@ -324,6 +324,11 @@ public class Connection {
if (this.connectionProperties.getNetworkCache() != null) { if (this.connectionProperties.getNetworkCache() != null) {
builder.networkCache(this.connectionProperties.getNetworkCache()); builder.networkCache(this.connectionProperties.getNetworkCache());
} }
if (this.connectionProperties.getCustomIceServers() != null && !this.connectionProperties.getCustomIceServers().isEmpty()) {
for (IceServerProperties iceServerProperties: this.connectionProperties.getCustomIceServers()) {
builder.addCustomIceServer(iceServerProperties);
}
}
this.connectionProperties = builder.build(); this.connectionProperties = builder.build();
} }
@ -417,6 +422,24 @@ public class Connection {
? OpenViduRole.valueOf(json.get("role").getAsString()) ? OpenViduRole.valueOf(json.get("role").getAsString())
: null; : null;
List<IceServerProperties> customIceServers = new ArrayList<>();
if (json.has("customIceServers") && json.get("customIceServers").isJsonArray()) {
JsonArray customIceServersJsonArray = json.get("customIceServers").getAsJsonArray();
customIceServersJsonArray.forEach(iceJsonElem -> {
JsonObject iceJsonObj = iceJsonElem.getAsJsonObject();
String url = (iceJsonObj.has("url") && !iceJsonObj.get("url").isJsonNull())
? json.get("url").getAsString()
: null;
String username = (iceJsonObj.has("username") && !iceJsonObj.get("username").isJsonNull())
? json.get("username").getAsString()
: null;
String credential = (iceJsonObj.has("credential") && !iceJsonObj.get("credential").isJsonNull())
? json.get("credential").getAsString()
: null;
customIceServers.add(new IceServerProperties.Builder().url(url).username(username).credential(credential).build());
});
}
// IPCAM // IPCAM
String rtspUri = (json.has("rtspUri") && !json.get("rtspUri").isJsonNull()) ? json.get("rtspUri").getAsString() String rtspUri = (json.has("rtspUri") && !json.get("rtspUri").isJsonNull()) ? json.get("rtspUri").getAsString()
: null; : null;
@ -431,25 +454,6 @@ public class Connection {
? json.get("networkCache").getAsInt() ? json.get("networkCache").getAsInt()
: null; : null;
// External Ice Servers
List<IceServerProperties> customIceServers = new ArrayList<>();
if (json.has("customIceServers") && json.get("customIceServers").isJsonArray()) {
JsonArray customIceServersJsonArray = json.get("customIceServers").getAsJsonArray();
customIceServersJsonArray.forEach(iceJsonElem -> {
JsonObject iceJsonObj = iceJsonElem.getAsJsonObject();
String url = (iceJsonObj.has("urls") && !iceJsonObj.get("urls").isJsonNull())
? json.get("urls").getAsString()
: null;
String username = (iceJsonObj.has("username") && !iceJsonObj.get("username").isJsonNull())
? json.get("username").getAsString()
: null;
String credential = (iceJsonObj.has("credential") && !iceJsonObj.get("credential").isJsonNull())
? json.get("credential").getAsString()
: null;
customIceServers.add(new IceServerProperties.Builder().url(url).username(username).credential(credential).build());
});
}
this.connectionProperties = new ConnectionProperties(type, data, record, role, null, rtspUri, adaptativeBitrate, this.connectionProperties = new ConnectionProperties(type, data, record, role, null, rtspUri, adaptativeBitrate,
onlyPlayWithSubscribers, networkCache, customIceServers); onlyPlayWithSubscribers, networkCache, customIceServers);

View File

@ -399,6 +399,12 @@ public class ConnectionProperties {
} else { } else {
json.add("kurentoOptions", JsonNull.INSTANCE); json.add("kurentoOptions", JsonNull.INSTANCE);
} }
JsonArray customIceServersJsonList = new JsonArray();
customIceServers.forEach((customIceServer) -> {
customIceServersJsonList.add(customIceServer.toJson());
});
json.add("customIceServers", customIceServersJsonList);
// IPCAM // IPCAM
if (getRtspUri() != null) { if (getRtspUri() != null) {
json.addProperty("rtspUri", getRtspUri()); json.addProperty("rtspUri", getRtspUri());
@ -420,14 +426,6 @@ public class ConnectionProperties {
} else { } else {
json.add("networkCache", JsonNull.INSTANCE); json.add("networkCache", JsonNull.INSTANCE);
} }
// Ice Servers
JsonArray customIceServersJsonList = new JsonArray();
customIceServers.forEach((customIceServer) -> {
customIceServersJsonList.add(customIceServer.toJson());
});
json.add("customIceServers", customIceServersJsonList);
return json; return json;
} }

View File

@ -1,7 +1,11 @@
package io.openvidu.java.client; package io.openvidu.java.client;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import org.apache.commons.validator.routines.DomainValidator;
import org.apache.commons.validator.routines.InetAddressValidator;
import java.net.Inet6Address;
import java.net.UnknownHostException;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -37,7 +41,7 @@ public class IceServerProperties {
*/ */
public JsonObject toJson() { public JsonObject toJson() {
JsonObject json = new JsonObject(); JsonObject json = new JsonObject();
json.addProperty("urls", getUrl()); json.addProperty("url", getUrl());
if (getUsername() != null && !getUsername().isEmpty()) { if (getUsername() != null && !getUsername().isEmpty()) {
json.addProperty("username", getUsername()); json.addProperty("username", getUsername());
} }
@ -74,13 +78,15 @@ public class IceServerProperties {
throw new IllegalArgumentException("External turn url cannot be null"); throw new IllegalArgumentException("External turn url cannot be null");
} }
this.checkValidStunTurn(this.url); this.checkValidStunTurn(this.url);
if (this.username == null ^ this.credential == null) { if (this.url.startsWith("turn")) {
// If one is null when the other is defined, fail... if ((this.username == null || this.credential == null)) {
throw new IllegalArgumentException("You need to define username and credentials if you define one of them"); throw new IllegalArgumentException("Credentials must be defined while using turn");
} }
if (this.username != null && this.credential != null && this.url.startsWith("stun")) { } else if (this.url.startsWith("stun")) {
// Credentials can not be defined using stun if (this.username != null || this.credential != null) {
throw new IllegalArgumentException("Credentials can not be defined while using stun."); // Credentials can not be defined using stun
throw new IllegalArgumentException("Credentials can not be defined while using stun.");
}
} }
return new IceServerProperties(this.url, this.username, this.credential); return new IceServerProperties(this.url, this.username, this.credential);
} }
@ -138,32 +144,88 @@ public class IceServerProperties {
// Check if port is defined // Check if port is defined
int portColon = hostAndPort.indexOf(':'); int portColon = hostAndPort.indexOf(':');
if (portColon != -1) { // IPv6 are defined between brackets
String[] splittedHostAndPort = hostAndPort.split(":"); int startIpv6Index = hostAndPort.indexOf('[');
if (splittedHostAndPort.length != 2) { int endIpv6Index = hostAndPort.indexOf(']');
throw new IllegalArgumentException("Host or port are not correctly " + if (startIpv6Index == -1 ^ endIpv6Index == -1) {
"defined in STUN/TURN uri: '" + uri + "'"); throw new IllegalArgumentException("Not closed bracket '[' or ']' in uri: " + uri);
}
String host = splittedHostAndPort[0];
String port = splittedHostAndPort[1];
// Check if host is defined. Valid Host (Domain or IP) will be done at server side
if (host == null || host.isEmpty()) {
throw new IllegalArgumentException("Host defined in '" + uri + "' is empty or null");
}
if (port == null || port.isEmpty()) {
throw new IllegalArgumentException("Port defined in '" + uri + "' is empty or null");
}
try {
int parsedPort = Integer.parseInt(port);
if (parsedPort <= 0 || parsedPort > 65535) {
throw new IllegalArgumentException("The port defined in '" + uri + "' is not a valid port number");
}
} catch (NumberFormatException e) {
throw new IllegalArgumentException("The port defined in '" + uri + "' is not a number");
}
} }
if (portColon != -1) {
if (startIpv6Index == -1 && endIpv6Index == -1) {
// If Ipv4 and port defined
String[] splittedHostAndPort = hostAndPort.split(":");
if (splittedHostAndPort.length != 2) {
throw new IllegalArgumentException("Host or port are not correctly " +
"defined in STUN/TURN uri: '" + uri + "'");
}
String host = splittedHostAndPort[0];
String port = splittedHostAndPort[1];
// Check if host is defined. Valid Host (Domain or IP) will be done at server side
checkHostAndPort(uri, host, port);
} else {
// If portColon is found and Ipv6
String ipv6 = hostAndPort.substring(startIpv6Index + 1, endIpv6Index);
String auxPort = hostAndPort.substring(endIpv6Index + 1);
if (auxPort.startsWith(":")) {
if (auxPort.length() == 1) {
throw new IllegalArgumentException("Host or port are not correctly defined in STUN/TURN uri: " + uri);
}
// If port is defined
// Get port without colon and check host and port
String host = ipv6;
String port = auxPort.substring(1);
checkHostAndPort(uri, host, port);
} else if (auxPort.length() > 0) {
// If auxPort = 0, no port is defined
throw new IllegalArgumentException("Port is not specified correctly after IPv6 in uri: '" + uri + "'");
}
}
} else {
// If portColon not found, only host is defined
String host = hostAndPort;
checkHost(uri, host);
}
}
private void checkHost(String uri, String host) {
if (host == null || host.isEmpty()) {
throw new IllegalArgumentException("Host defined in '" + uri + "' is empty or null");
}
if (DomainValidator.getInstance().isValid(host)) {
return;
}
InetAddressValidator ipValidator = InetAddressValidator.getInstance();
if (ipValidator.isValid(host)) {
return;
}
try {
Inet6Address.getByName(host).getHostAddress();
return;
} catch (UnknownHostException e) {
throw new IllegalArgumentException("Is not a valid Internet Address (IP or Domain Name): '" + host + "'");
}
}
private void checkPort(String uri, String port) {
if (port == null || port.isEmpty()) {
throw new IllegalArgumentException("Port defined in '" + uri + "' is empty or null");
}
try {
int parsedPort = Integer.parseInt(port);
if (parsedPort <= 0 || parsedPort > 65535) {
throw new IllegalArgumentException("The port defined in '" + uri + "' is not a valid port number (0-65535)");
}
} catch (NumberFormatException e) {
throw new IllegalArgumentException("The port defined in '" + uri + "' is not a number (0-65535)");
}
}
private void checkHostAndPort(String uri, String host, String port) {
this.checkHost(uri, host);
this.checkPort(uri, port);
} }
} }

View File

@ -19,6 +19,7 @@ package io.openvidu.server.core;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@ -51,6 +52,7 @@ import io.openvidu.java.client.KurentoOptions;
import io.openvidu.java.client.OpenViduRole; import io.openvidu.java.client.OpenViduRole;
import io.openvidu.java.client.Recording; import io.openvidu.java.client.Recording;
import io.openvidu.java.client.SessionProperties; import io.openvidu.java.client.SessionProperties;
import io.openvidu.java.client.IceServerProperties;
import io.openvidu.server.cdr.CDREventRecordingStatusChanged; import io.openvidu.server.cdr.CDREventRecordingStatusChanged;
import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.config.OpenviduConfig;
import io.openvidu.server.coturn.CoturnCredentialsService; import io.openvidu.server.coturn.CoturnCredentialsService;
@ -329,13 +331,13 @@ public abstract class SessionManager {
} }
public Token newToken(Session session, OpenViduRole role, String serverMetadata, boolean record, public Token newToken(Session session, OpenViduRole role, String serverMetadata, boolean record,
KurentoOptions kurentoOptions) throws Exception { KurentoOptions kurentoOptions, List<IceServerProperties> customIceServers) throws Exception {
if (!formatChecker.isServerMetadataFormatCorrect(serverMetadata)) { if (!formatChecker.isServerMetadataFormatCorrect(serverMetadata)) {
log.error("Data invalid format"); log.error("Data invalid format");
throw new OpenViduException(Code.GENERIC_ERROR_CODE, "Data invalid format"); throw new OpenViduException(Code.GENERIC_ERROR_CODE, "Data invalid format");
} }
Token tokenObj = tokenGenerator.generateToken(session.getSessionId(), serverMetadata, record, role, Token tokenObj = tokenGenerator.generateToken(session.getSessionId(), serverMetadata, record, role,
kurentoOptions); kurentoOptions, customIceServers);
// Internal dev feature: allows customizing connectionId // Internal dev feature: allows customizing connectionId
if (serverMetadata.contains("openviduCustomConnectionId")) { if (serverMetadata.contains("openviduCustomConnectionId")) {

View File

@ -17,18 +17,17 @@
package io.openvidu.server.core; package io.openvidu.server.core;
import io.openvidu.java.client.*;
import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.RandomStringUtils;
import com.google.gson.JsonNull; import com.google.gson.JsonNull;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import io.openvidu.java.client.ConnectionProperties;
import io.openvidu.java.client.ConnectionType;
import io.openvidu.java.client.KurentoOptions;
import io.openvidu.java.client.OpenViduRole;
import io.openvidu.server.core.Participant.ParticipantStatus; import io.openvidu.server.core.Participant.ParticipantStatus;
import io.openvidu.server.coturn.TurnCredentials; import io.openvidu.server.coturn.TurnCredentials;
import java.util.List;
public class Token { public class Token {
private String token; private String token;
@ -77,7 +76,8 @@ public class Token {
this.updateConnectionProperties(connectionProperties.getType(), connectionProperties.getData(), newRecord, this.updateConnectionProperties(connectionProperties.getType(), connectionProperties.getData(), newRecord,
connectionProperties.getRole(), connectionProperties.getKurentoOptions(), connectionProperties.getRole(), connectionProperties.getKurentoOptions(),
connectionProperties.getRtspUri(), connectionProperties.adaptativeBitrate(), connectionProperties.getRtspUri(), connectionProperties.adaptativeBitrate(),
connectionProperties.onlyPlayWithSubscribers(), connectionProperties.getNetworkCache()); connectionProperties.onlyPlayWithSubscribers(), connectionProperties.getNetworkCache(),
connectionProperties.getCustomIceServers());
} }
public OpenViduRole getRole() { public OpenViduRole getRole() {
@ -88,7 +88,8 @@ public class Token {
this.updateConnectionProperties(connectionProperties.getType(), connectionProperties.getData(), this.updateConnectionProperties(connectionProperties.getType(), connectionProperties.getData(),
connectionProperties.record(), newRole, connectionProperties.getKurentoOptions(), connectionProperties.record(), newRole, connectionProperties.getKurentoOptions(),
connectionProperties.getRtspUri(), connectionProperties.adaptativeBitrate(), connectionProperties.getRtspUri(), connectionProperties.adaptativeBitrate(),
connectionProperties.onlyPlayWithSubscribers(), connectionProperties.getNetworkCache()); connectionProperties.onlyPlayWithSubscribers(), connectionProperties.getNetworkCache(),
connectionProperties.getCustomIceServers());
} }
public KurentoOptions getKurentoOptions() { public KurentoOptions getKurentoOptions() {
@ -118,6 +119,10 @@ public class Token {
public String getConnectionId() { public String getConnectionId() {
return connectionId; return connectionId;
} }
public List<IceServerProperties> getCustomIceServers() {
return this.connectionProperties.getCustomIceServers();
}
public void setConnectionId(String connectionId) { public void setConnectionId(String connectionId) {
this.connectionId = connectionId; this.connectionId = connectionId;
@ -178,7 +183,7 @@ public class Token {
private void updateConnectionProperties(ConnectionType type, String data, Boolean record, OpenViduRole role, private void updateConnectionProperties(ConnectionType type, String data, Boolean record, OpenViduRole role,
KurentoOptions kurentoOptions, String rtspUri, Boolean adaptativeBitrate, Boolean onlyPlayWithSubscribers, KurentoOptions kurentoOptions, String rtspUri, Boolean adaptativeBitrate, Boolean onlyPlayWithSubscribers,
Integer networkCache) { Integer networkCache, List<IceServerProperties> iceServerProperties) {
ConnectionProperties.Builder builder = new ConnectionProperties.Builder(); ConnectionProperties.Builder builder = new ConnectionProperties.Builder();
if (type != null) { if (type != null) {
builder.type(type); builder.type(type);
@ -207,6 +212,11 @@ public class Token {
if (networkCache != null) { if (networkCache != null) {
builder.networkCache(networkCache); builder.networkCache(networkCache);
} }
if (iceServerProperties != null) {
for (IceServerProperties customIceServer: iceServerProperties) {
builder.addCustomIceServer(customIceServer);
}
}
this.connectionProperties = builder.build(); this.connectionProperties = builder.build();
} }

View File

@ -24,12 +24,15 @@ import io.openvidu.java.client.ConnectionProperties;
import io.openvidu.java.client.ConnectionType; import io.openvidu.java.client.ConnectionType;
import io.openvidu.java.client.KurentoOptions; import io.openvidu.java.client.KurentoOptions;
import io.openvidu.java.client.OpenViduRole; import io.openvidu.java.client.OpenViduRole;
import io.openvidu.java.client.IceServerProperties;
import io.openvidu.server.OpenViduServer; import io.openvidu.server.OpenViduServer;
import io.openvidu.server.config.OpenviduBuildInfo; import io.openvidu.server.config.OpenviduBuildInfo;
import io.openvidu.server.config.OpenviduConfig; import io.openvidu.server.config.OpenviduConfig;
import io.openvidu.server.coturn.CoturnCredentialsService; import io.openvidu.server.coturn.CoturnCredentialsService;
import io.openvidu.server.coturn.TurnCredentials; import io.openvidu.server.coturn.TurnCredentials;
import java.util.List;
public class TokenGenerator { public class TokenGenerator {
@Autowired @Autowired
@ -42,7 +45,7 @@ public class TokenGenerator {
protected OpenviduBuildInfo openviduBuildConfig; protected OpenviduBuildInfo openviduBuildConfig;
public Token generateToken(String sessionId, String serverMetadata, boolean record, OpenViduRole role, public Token generateToken(String sessionId, String serverMetadata, boolean record, OpenViduRole role,
KurentoOptions kurentoOptions) throws Exception { KurentoOptions kurentoOptions, List<IceServerProperties> customIceServers) throws Exception {
String token = OpenViduServer.wsUrl; String token = OpenViduServer.wsUrl;
token += "?sessionId=" + sessionId; token += "?sessionId=" + sessionId;
token += "&token=" + IdentifierPrefixes.TOKEN_ID + RandomStringUtils.randomAlphabetic(1).toUpperCase() token += "&token=" + IdentifierPrefixes.TOKEN_ID + RandomStringUtils.randomAlphabetic(1).toUpperCase()
@ -51,8 +54,13 @@ public class TokenGenerator {
if (this.openviduConfig.isTurnadminAvailable()) { if (this.openviduConfig.isTurnadminAvailable()) {
turnCredentials = coturnCredentialsService.createUser(); turnCredentials = coturnCredentialsService.createUser();
} }
ConnectionProperties connectionProperties = new ConnectionProperties.Builder().type(ConnectionType.WEBRTC) ConnectionProperties.Builder connectionPropertiesBuilder = new ConnectionProperties.Builder()
.data(serverMetadata).record(record).role(role).kurentoOptions(kurentoOptions).build(); .type(ConnectionType.WEBRTC).data(serverMetadata).record(record).role(role)
.kurentoOptions(kurentoOptions);
for (IceServerProperties customIceServer: customIceServers) {
connectionPropertiesBuilder.addCustomIceServer(customIceServer);
}
ConnectionProperties connectionProperties = connectionPropertiesBuilder.build();
return new Token(token, sessionId, connectionProperties, turnCredentials); return new Token(token, sessionId, connectionProperties, turnCredentials);
} }

View File

@ -21,6 +21,7 @@ import java.net.MalformedURLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -653,7 +654,7 @@ public class SessionRestController {
try { try {
Token token = sessionManager.newToken(session, connectionProperties.getRole(), Token token = sessionManager.newToken(session, connectionProperties.getRole(),
connectionProperties.getData(), connectionProperties.record(), connectionProperties.getData(), connectionProperties.record(),
connectionProperties.getKurentoOptions()); connectionProperties.getKurentoOptions(), connectionProperties.getCustomIceServers());
return new ResponseEntity<>(token.toJsonAsParticipant().toString(), RestUtils.getResponseHeaders(), return new ResponseEntity<>(token.toJsonAsParticipant().toString(), RestUtils.getResponseHeaders(),
HttpStatus.OK); HttpStatus.OK);
} catch (Exception e) { } catch (Exception e) {
@ -909,7 +910,7 @@ public class SessionRestController {
JsonArray customIceServersJsonArray = null; JsonArray customIceServersJsonArray = null;
if (params.get("customIceServers") != null) { if (params.get("customIceServers") != null) {
try { try {
customIceServersJsonArray = new Gson().toJsonTree(params.get("customIceServers"), Map.class) customIceServersJsonArray = new Gson().toJsonTree(params.get("customIceServers"), List.class)
.getAsJsonArray(); .getAsJsonArray();
} catch (Exception e) { } catch (Exception e) {
throw new Exception("Error in parameter 'customIceServersJson'. It is not a valid JSON object"); throw new Exception("Error in parameter 'customIceServersJson'. It is not a valid JSON object");
@ -920,7 +921,7 @@ public class SessionRestController {
for (int i = 0; i < customIceServersJsonArray.size(); i++) { for (int i = 0; i < customIceServersJsonArray.size(); i++) {
JsonObject customIceServerJson = customIceServersJsonArray.get(i).getAsJsonObject(); JsonObject customIceServerJson = customIceServersJsonArray.get(i).getAsJsonObject();
IceServerProperties.Builder iceServerPropertiesBuilder = new IceServerProperties.Builder(); IceServerProperties.Builder iceServerPropertiesBuilder = new IceServerProperties.Builder();
iceServerPropertiesBuilder.url(customIceServerJson.get("urls").getAsString()); iceServerPropertiesBuilder.url(customIceServerJson.get("url").getAsString());
if (customIceServerJson.has("username")) { if (customIceServerJson.has("username")) {
iceServerPropertiesBuilder.username(customIceServerJson.get("username").getAsString()); iceServerPropertiesBuilder.username(customIceServerJson.get("username").getAsString());
} }
@ -931,7 +932,7 @@ public class SessionRestController {
builder.addCustomIceServer(iceServerProperties); builder.addCustomIceServer(iceServerProperties);
} }
} catch (Exception e) { } catch (Exception e) {
throw new Exception("Type error in some parameter of 'kurentoOptions': " + e.getMessage()); throw new Exception("Type error in some parameter of 'customIceServers': " + e.getMessage());
} }
} }

View File

@ -0,0 +1,251 @@
package io.openvidu.server.test.unit;
import io.openvidu.java.client.IceServerProperties;
import org.junit.Test;
import org.junit.jupiter.api.DisplayName;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.*;
public class IceServerPropertiesTests {
@Test
@DisplayName("IceServerProperty exceptions tests")
public void iceServerPropertiesExceptionTest() {
// Wrong urls
notValidIceServerTest(
"wrongurl", null, null,
"Not a valid TURN/STUN uri provided. No colons found in: 'wrongurl'"
);
notValidIceServerTest(
"wrongurl", "anyuser", null,
"Not a valid TURN/STUN uri provided. No colons found in: 'wrongurl'"
);
notValidIceServerTest(
"wrongurl", null, "anypassword",
"Not a valid TURN/STUN uri provided. No colons found in: 'wrongurl'"
);
notValidIceServerTest(
"wrongurl", "anyuser", "anypassword",
"Not a valid TURN/STUN uri provided. No colons found in: 'wrongurl'"
);
// Wrong prefixes
notValidIceServerTest(
"turnss:wrongurl", null, null,
"The protocol 'turnss' is invalid. Only valid values are: [turn, turns] [stuns, stun]"
);
notValidIceServerTest(
"stunss:wrongurl", "anyuser", null,
"The protocol 'stunss' is invalid. Only valid values are: [turn, turns] [stuns, stun]"
);
notValidIceServerTest(
"anything:wrongurl", null, "anypassword",
"The protocol 'anything' is invalid. Only valid values are: [turn, turns] [stuns, stun]"
);
notValidIceServerTest(
":", null, null,
"The protocol '' is invalid. Only valid values are: [turn, turns] [stuns, stun]"
);
notValidIceServerTest(
"", null, null,
"Not a valid TURN/STUN uri provided. No colons found in: ''"
);
// Try invalid host and ports
notValidIceServerTest(
"stun:hostname.com:99a99", null, null,
"The port defined in 'stun:hostname.com:99a99' is not a number (0-65535)"
);
notValidIceServerTest(
"stun:hostname.com:-1", null, null,
"The port defined in 'stun:hostname.com:-1' is not a valid port number (0-65535)"
);
notValidIceServerTest(
"stun:hostname:port:more", null, null,
"Host or port are not correctly defined in STUN/TURN uri: 'stun:hostname:port:more'"
);
notValidIceServerTest(
"stun:hostname.com:port more", null, null,
"The port defined in 'stun:hostname.com:port more' is not a number (0-65535)"
);
notValidIceServerTest(
"stun:hostname.com:", null, null,
"Host or port are not correctly defined in STUN/TURN uri: 'stun:hostname.com:'"
);
notValidIceServerTest(
"stun:[1:2:3:4:5:6:7:8]junk:1000", null, null,
"Port is not specified correctly after IPv6 in uri: 'stun:[1:2:3:4:5:6:7:8]junk:1000'"
);
notValidIceServerTest(
"stun:[notvalid:]:1000", null, null,
"Is not a valid Internet Address (IP or Domain Name): 'notvalid:'"
);
notValidIceServerTest(
"stun::5555", null, null,
"Host defined in 'stun::5555' is empty or null"
);
notValidIceServerTest(
"stun:", null, null,
"Host defined in 'stun:' is empty or null"
);
// Illegal Uri tests according to RFC 3986 and RFC 7064 (URI schemes for STUN and TURN)
notValidIceServerTest(
"stun:/hostname.com", null, null,
"Is not a valid Internet Address (IP or Domain Name): '/hostname.com'"
);
notValidIceServerTest(
"stun:?hostname.com", null, null,
"STUN uri can't have any '?' query param"
);
notValidIceServerTest(
"stun:#hostname.com", null, null,
"Is not a valid Internet Address (IP or Domain Name): '#hostname.com'"
);
// illegal ?transport=xxx tests in turn uris
notValidIceServerTest(
"turn:hostname.com?transport=invalid", "anyuser", "anypassword",
"Wrong value specified in STUN/TURN uri: 'turn:hostname.com?transport=invalid'. Unique valid arguments after '?' are '?transport=tcp' or '?transport=udp"
);
notValidIceServerTest(
"turn:hostname.com?transport=", "anyuser", "anypassword",
"Wrong value specified in STUN/TURN uri: 'turn:hostname.com?transport='. Unique valid arguments after '?' are '?transport=tcp' or '?transport=udp"
);
notValidIceServerTest(
"turn:hostname.com?=", "anyuser", "anypassword",
"Wrong value specified in STUN/TURN uri: 'turn:hostname.com?='. Unique valid arguments after '?' are '?transport=tcp' or '?transport=udp"
);
notValidIceServerTest(
"turn:hostname.com?", "anyuser", "anypassword",
"Wrong value specified in STUN/TURN uri: 'turn:hostname.com?'. Unique valid arguments after '?' are '?transport=tcp' or '?transport=udp"
);
notValidIceServerTest(
"?", "anyuser", "anypassword",
"Not a valid TURN/STUN uri provided. No colons found in: '?'"
);
// Transport can not be defined in STUN
notValidIceServerTest(
"stun:hostname.com?transport=tcp", null, null,
"STUN uri can't have any '?' query param"
);
notValidIceServerTest(
"stun:hostname.com?transport=udp", null, null,
"STUN uri can't have any '?' query param"
);
// Stun can not have credentials defined
notValidIceServerTest(
"stun:hostname.com", "username", "credential",
"Credentials can not be defined while using stun."
);
notValidIceServerTest(
"stun:hostname.com", "username", "credential",
"Credentials can not be defined while using stun."
);
notValidIceServerTest(
"stun:hostname.com", "username", null,
"Credentials can not be defined while using stun."
);
notValidIceServerTest(
"stun:hostname.com", null, "credential",
"Credentials can not be defined while using stun."
);
// Turn must have credentials
notValidIceServerTest(
"turn:hostname.com", null, null,
"Credentials must be defined while using turn"
);
notValidIceServerTest(
"turn:hostname.com", "username", null,
"Credentials must be defined while using turn"
);
notValidIceServerTest(
"turn:hostname.com", null, "credential",
"Credentials must be defined while using turn"
);
}
@Test
@DisplayName("IceServerProperty exceptions tests")
public void iceServerPropertiesValidTest() {
// Stun and stuns
validIceServerTest("stun:hostname.com", null, null);
validIceServerTest("stuns:hostname.com", null, null);
// Turn and turns
validIceServerTest("turn:hostname.com", "anyuser", "credential");
validIceServerTest("turns:hostname.com", "anyuser", "credential");
// Test IPv4/IPv6/hostname and with/without port
validIceServerTest("stun:1.2.3.4:1234", null, null);
validIceServerTest("stun:[1:2:3:4:5:6:7:8]:4321", null, null);
validIceServerTest("stun:hostname.com:9999", null, null);
validIceServerTest("stun:1.2.3.4", null, null);
validIceServerTest("stun:[1:2:3:4:5:6:7:8]", null, null);
validIceServerTest("stuns:1.2.3.4:1234", null, null);
validIceServerTest("stuns:[1:2:3:4:5:6:7:8]:4321", null, null);
validIceServerTest("stuns:hostname.com:9999", null, null);
validIceServerTest("stuns:1.2.3.4", null, null);
validIceServerTest("stuns:[1:2:3:4:5:6:7:8]", null, null);
validIceServerTest("turn:1.2.3.4:1234", "anyuser", "credential");
validIceServerTest("turn:[1:2:3:4:5:6:7:8]:4321", "anyuser", "credential");
validIceServerTest("turn:hostname.com:9999", "anyuser", "credential");
validIceServerTest("turn:1.2.3.4", "anyuser", "credential");
validIceServerTest("turn:[1:2:3:4:5:6:7:8]", "anyuser", "credential");
validIceServerTest("turns:1.2.3.4:1234", "anyuser", "credential");
validIceServerTest("turns:[1:2:3:4:5:6:7:8]:4321", "anyuser", "credential");
validIceServerTest("turns:hostname.com:9999", "anyuser", "credential");
validIceServerTest("turns:1.2.3.4", "anyuser", "credential");
validIceServerTest("turns:[1:2:3:4:5:6:7:8]", "anyuser", "credential");
// Test valid ?transport=tcp or ?transport=udp
validIceServerTest("turn:hostname.com:1234?transport=tcp", "anyuser", "credential");
validIceServerTest("turn:hostname.com?transport=udp", "anyuser", "credential");
validIceServerTest("turn:1.2.3.4:1234?transport=tcp", "anyuser", "credential");
validIceServerTest("turn:1.2.3.4?transport=udp", "anyuser", "credential");
validIceServerTest("turn:[1:2:3:4:5:6:7:8]:4321?transport=udp", "anyuser", "credential");
validIceServerTest("turn:[1:2:3:4:5:6:7:8]?transport=udp", "anyuser", "credential");
}
private void validIceServerTest(String url, String username, String credential) {
assertDoesNotThrow(() -> {
IceServerProperties.Builder iceServerPropertiesBuilder = new IceServerProperties.Builder().url(url);
if (username != null) {
iceServerPropertiesBuilder.username(username);
}
if (credential != null) {
iceServerPropertiesBuilder.credential(credential);
}
IceServerProperties iceServerProperties = iceServerPropertiesBuilder.build();
assertEquals(url, iceServerProperties.getUrl());
if (username != null) {
assertEquals(username, iceServerProperties.getUsername());
}
if (credential != null) {
assertEquals(credential, iceServerProperties.getCredential());
}
});
}
private void notValidIceServerTest(String url, String username, String credential, String expectedMessage) {
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
IceServerProperties.Builder iceServerPropertiesBuilder = new IceServerProperties.Builder().url(url);
if (username != null) {
iceServerPropertiesBuilder.username(username);
}
if (credential != null) {
iceServerPropertiesBuilder.credential(credential);
}
iceServerPropertiesBuilder.build();
});
String actualMessage = exception.getMessage();
assertEquals(actualMessage, expectedMessage);
}
}