mirror of https://github.com/OpenVidu/openvidu.git
Initial logic in openvidu-java-client to add to the ConnectionProperties class a new 'customIceServers' parameter
parent
e56ac82749
commit
d7eae78372
|
@ -25,7 +25,9 @@ import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonParser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See {@link io.openvidu.java.client.Session#getConnections()}
|
* See {@link io.openvidu.java.client.Session#getConnections()}
|
||||||
|
@ -428,8 +430,28 @@ public class Connection {
|
||||||
Integer networkCache = (json.has("networkCache") && !json.get("networkCache").isJsonNull())
|
Integer networkCache = (json.has("networkCache") && !json.get("networkCache").isJsonNull())
|
||||||
? 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);
|
onlyPlayWithSubscribers, networkCache, customIceServers);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
package io.openvidu.java.client;
|
package io.openvidu.java.client;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
import com.google.gson.JsonNull;
|
import com.google.gson.JsonNull;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See
|
* See
|
||||||
* {@link io.openvidu.java.client.Session#createConnection(ConnectionProperties)}
|
* {@link io.openvidu.java.client.Session#createConnection(ConnectionProperties)}
|
||||||
|
@ -22,6 +26,9 @@ public class ConnectionProperties {
|
||||||
private Boolean onlyPlayWithSubscribers;
|
private Boolean onlyPlayWithSubscribers;
|
||||||
private Integer networkCache;
|
private Integer networkCache;
|
||||||
|
|
||||||
|
// External Turn Service
|
||||||
|
private List<IceServerProperties> customIceServers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Builder for {@link io.openvidu.java.client.ConnectionProperties}
|
* Builder for {@link io.openvidu.java.client.ConnectionProperties}
|
||||||
|
@ -42,12 +49,16 @@ public class ConnectionProperties {
|
||||||
private Boolean onlyPlayWithSubscribers;
|
private Boolean onlyPlayWithSubscribers;
|
||||||
private Integer networkCache;
|
private Integer networkCache;
|
||||||
|
|
||||||
|
// External Turn Service
|
||||||
|
private List<IceServerProperties> customIceServers = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder for {@link io.openvidu.java.client.ConnectionProperties}.
|
* Builder for {@link io.openvidu.java.client.ConnectionProperties}.
|
||||||
*/
|
*/
|
||||||
public ConnectionProperties build() {
|
public ConnectionProperties build() {
|
||||||
return new ConnectionProperties(this.type, this.data, this.record, this.role, this.kurentoOptions,
|
return new ConnectionProperties(this.type, this.data, this.record, this.role, this.kurentoOptions,
|
||||||
this.rtspUri, this.adaptativeBitrate, this.onlyPlayWithSubscribers, this.networkCache);
|
this.rtspUri, this.adaptativeBitrate, this.onlyPlayWithSubscribers, this.networkCache,
|
||||||
|
this.customIceServers);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -219,11 +230,17 @@ public class ConnectionProperties {
|
||||||
this.networkCache = networkCache;
|
this.networkCache = networkCache;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Comment
|
||||||
|
public Builder addCustomIceServer(IceServerProperties iceServerProperties) {
|
||||||
|
this.customIceServers.add(iceServerProperties);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionProperties(ConnectionType type, String data, Boolean record, OpenViduRole role,
|
ConnectionProperties(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> customIceServers) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.record = record;
|
this.record = record;
|
||||||
|
@ -233,6 +250,7 @@ public class ConnectionProperties {
|
||||||
this.adaptativeBitrate = adaptativeBitrate;
|
this.adaptativeBitrate = adaptativeBitrate;
|
||||||
this.onlyPlayWithSubscribers = onlyPlayWithSubscribers;
|
this.onlyPlayWithSubscribers = onlyPlayWithSubscribers;
|
||||||
this.networkCache = networkCache;
|
this.networkCache = networkCache;
|
||||||
|
this.customIceServers = customIceServers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -346,6 +364,11 @@ public class ConnectionProperties {
|
||||||
return this.networkCache;
|
return this.networkCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Comment
|
||||||
|
public List<IceServerProperties> getCustomIceServers() {
|
||||||
|
return new ArrayList<>(this.customIceServers);
|
||||||
|
}
|
||||||
|
|
||||||
public JsonObject toJson(String sessionId) {
|
public JsonObject toJson(String sessionId) {
|
||||||
JsonObject json = new JsonObject();
|
JsonObject json = new JsonObject();
|
||||||
json.addProperty("session", sessionId);
|
json.addProperty("session", sessionId);
|
||||||
|
@ -397,6 +420,14 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
package io.openvidu.java.client;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class IceServerProperties {
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
private String username;
|
||||||
|
private String credential;
|
||||||
|
|
||||||
|
public String getUrl() {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCredential() {
|
||||||
|
return credential;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IceServerProperties(String url, String username, String credential) {
|
||||||
|
this.url = url;
|
||||||
|
this.username = username;
|
||||||
|
this.credential = credential;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ice server properties following RTCIceServers format:
|
||||||
|
* https://developer.mozilla.org/en-US/docs/Web/API/RTCIceServer/urls
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public JsonObject toJson() {
|
||||||
|
JsonObject json = new JsonObject();
|
||||||
|
json.addProperty("urls", getUrl());
|
||||||
|
if (getUsername() != null && !getUsername().isEmpty()) {
|
||||||
|
json.addProperty("username", getUsername());
|
||||||
|
}
|
||||||
|
if (getCredential() != null && !getCredential().isEmpty()) {
|
||||||
|
json.addProperty("credential", getCredential());
|
||||||
|
}
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Builder {
|
||||||
|
|
||||||
|
private String url;
|
||||||
|
private String username;
|
||||||
|
private String credential;
|
||||||
|
|
||||||
|
public IceServerProperties.Builder url(String url) {
|
||||||
|
this.url = url;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IceServerProperties.Builder username(String userName) {
|
||||||
|
this.username = userName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IceServerProperties.Builder credential(String credential) {
|
||||||
|
this.credential = credential;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public IceServerProperties build() throws IllegalArgumentException {
|
||||||
|
if (this.url == null) {
|
||||||
|
throw new IllegalArgumentException("External turn url cannot be null");
|
||||||
|
}
|
||||||
|
this.checkValidStunTurn(this.url);
|
||||||
|
if (this.username == null ^ this.credential == null) {
|
||||||
|
// If one is null when the other is defined, fail...
|
||||||
|
throw new IllegalArgumentException("You need to define username and credentials if you define one of them");
|
||||||
|
}
|
||||||
|
if (this.username != null && this.credential != null && this.url.startsWith("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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Parsing Turn Stun Uri based on:
|
||||||
|
* - https://datatracker.ietf.org/doc/html/rfc7065#section-3.1
|
||||||
|
* - https://datatracker.ietf.org/doc/html/rfc7064#section-3.1
|
||||||
|
*/
|
||||||
|
private void checkValidStunTurn(String uri) throws IllegalArgumentException {
|
||||||
|
final String TCP_TRANSPORT_SUFFIX = "?transport=tcp";
|
||||||
|
final String UDP_TRANSPORT_SUFFIX = "?transport=udp";
|
||||||
|
|
||||||
|
// Protocols which accepts transport=tcp and transport=udp
|
||||||
|
final Set<String> TURN_PROTOCOLS = new HashSet<>(Arrays.asList(
|
||||||
|
"turn",
|
||||||
|
"turns"
|
||||||
|
));
|
||||||
|
final Set<String> STUN_PROTOCOLS = new HashSet<>(Arrays.asList(
|
||||||
|
"stun",
|
||||||
|
"stuns"
|
||||||
|
));
|
||||||
|
|
||||||
|
// Fails if no colons
|
||||||
|
int firstColonPos = uri.indexOf(':');
|
||||||
|
if (firstColonPos == -1) {
|
||||||
|
throw new IllegalArgumentException("Not a valid TURN/STUN uri provided. " +
|
||||||
|
"No colons found in: '" + uri + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get protocol and check
|
||||||
|
String protocol = uri.substring(0, firstColonPos);
|
||||||
|
if (!TURN_PROTOCOLS.contains(protocol) && !STUN_PROTOCOLS.contains(protocol)) {
|
||||||
|
throw new IllegalArgumentException("The protocol '" + protocol + "' is invalid. Only valid values are: "
|
||||||
|
+ TURN_PROTOCOLS + " " + STUN_PROTOCOLS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if query param with transport exist
|
||||||
|
int qmarkPos = uri.indexOf('?');
|
||||||
|
String hostAndPort = uri.substring(firstColonPos + 1);
|
||||||
|
if (qmarkPos != -1) {
|
||||||
|
if (TURN_PROTOCOLS.contains(protocol)) {
|
||||||
|
// Only Turn uses transport arg
|
||||||
|
String rawTransportType = uri.substring(qmarkPos);
|
||||||
|
hostAndPort = uri.substring(firstColonPos + 1, qmarkPos);
|
||||||
|
if (!TCP_TRANSPORT_SUFFIX.equals(rawTransportType) && !UDP_TRANSPORT_SUFFIX.equals(rawTransportType)) {
|
||||||
|
// If other argument rather than transport is specified, it is a wrong query for a STUN/TURN uri
|
||||||
|
throw new IllegalArgumentException("Wrong value specified in STUN/TURN uri: '"
|
||||||
|
+ uri + "'. " + "Unique valid arguments after '?' are '"
|
||||||
|
+ TCP_TRANSPORT_SUFFIX + "' or '" + UDP_TRANSPORT_SUFFIX);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("STUN uri can't have any '?' query param");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if port is defined
|
||||||
|
int portColon = hostAndPort.indexOf(':');
|
||||||
|
if (portColon != -1) {
|
||||||
|
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
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue