Check openvidu-browser and openvidu-server compatibility

pull/658/head
pabloFuente 2021-10-27 14:04:19 +02:00
parent 1a244f68cd
commit 8e418bfd16
8 changed files with 235 additions and 12 deletions

View File

@ -5,6 +5,7 @@
"hark": "1.2.3",
"jsnlog": "2.30.0",
"platform": "1.3.6",
"semver": "7.3.5",
"uuid": "8.3.2",
"wolfy87-eventemitter": "5.2.9"
},

View File

@ -44,6 +44,8 @@ import { OpenViduError, OpenViduErrorName } from '../OpenViduInternal/Enums/Open
import { VideoInsertMode } from '../OpenViduInternal/Enums/VideoInsertMode';
import { OpenViduLogger } from '../OpenViduInternal/Logger/OpenViduLogger';
import { PlatformUtils } from '../OpenViduInternal/Utils/Platform';
import semverMajor = require('semver/functions/major');
import semverMinor = require('semver/functions/minor');
/**
* @hidden
@ -1250,6 +1252,7 @@ export class Session extends EventDispatcher {
token: (!!token) ? token : '',
session: this.sessionId,
platform: !!platform.getDescription() ? platform.getDescription() : 'unknown',
sdkVersion: this.openvidu.libraryVersion,
metadata: !!this.options.metadata ? this.options.metadata : '',
secret: this.openvidu.getSecret(),
recorder: this.openvidu.getRecorder()
@ -1547,10 +1550,14 @@ export class Session extends EventDispatcher {
if (opts.life != null) {
this.openvidu.life = opts.life;
}
if (opts.version !== this.openvidu.libraryVersion) {
logger.warn('OpenVidu Server (' + opts.version +
') and OpenVidu Browser (' + this.openvidu.libraryVersion +
') versions do NOT match. There may be incompatibilities')
const minorDifference: number = semverMinor(opts.version) - semverMinor(this.openvidu.libraryVersion);
if ((semverMajor(opts.version) !== semverMajor(this.openvidu.libraryVersion)) || !(minorDifference == 0 || minorDifference == 1)) {
logger.error(`openvidu-browser (${this.openvidu.libraryVersion}) and openvidu-server (${opts.version}) versions are incompatible. `
+ 'Errors are likely to occur. openvidu-browser SDK is only compatible with the same version or the immediately following minor version of an OpenVidu deployment');
} else if (minorDifference == 1) {
logger.warn(`openvidu-browser version ${this.openvidu.libraryVersion} does not match openvidu-server version ${opts.version}. `
+ `These versions are still compatible with each other, but openvidu-browser version must be updated as soon as possible to ${semverMajor(opts.version)}.${semverMinor(opts.version)}.x. `
+ `This client using openvidu-browser ${this.openvidu.libraryVersion} will become incompatible with the next release of openvidu-server`);
}
}

View File

@ -38,6 +38,7 @@ public class ProtocolElements {
public static final String JOINROOM_METADATA_PARAM = "metadata";
public static final String JOINROOM_SECRET_PARAM = "secret";
public static final String JOINROOM_PLATFORM_PARAM = "platform";
public static final String JOINROOM_SDKVERSION_PARAM = "sdkVersion";
public static final String JOINROOM_RECORDER_PARAM = "recorder";
public static final String JOINROOM_PEERID_PARAM = "id";

View File

@ -332,6 +332,11 @@
<artifactId>openvidu-java-client</artifactId>
<version>${version.openvidu.java.client}</version>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-artifact</artifactId>
<version>${version.maven.artifact}</version>
</dependency>
<!-- Test dependencies -->

View File

@ -27,6 +27,7 @@ import java.util.concurrent.ConcurrentMap;
import javax.servlet.http.HttpSession;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.kurento.jsonrpc.DefaultJsonRpcHandler;
import org.kurento.jsonrpc.Session;
import org.kurento.jsonrpc.Transaction;
@ -46,6 +47,7 @@ import io.openvidu.client.OpenViduException;
import io.openvidu.client.OpenViduException.Code;
import io.openvidu.client.internal.ProtocolElements;
import io.openvidu.java.client.ConnectionProperties;
import io.openvidu.server.config.OpenviduBuildInfo;
import io.openvidu.server.config.OpenviduConfig;
import io.openvidu.server.core.EndReason;
import io.openvidu.server.core.IdentifierPrefixes;
@ -55,6 +57,8 @@ import io.openvidu.server.core.SessionManager;
import io.openvidu.server.core.Token;
import io.openvidu.server.utils.GeoLocation;
import io.openvidu.server.utils.GeoLocationByIp;
import io.openvidu.server.utils.VersionComparator;
import io.openvidu.server.utils.VersionComparator.VersionMismatchException;
public class RpcHandler extends DefaultJsonRpcHandler<JsonObject> {
@ -63,6 +67,9 @@ public class RpcHandler extends DefaultJsonRpcHandler<JsonObject> {
@Autowired
OpenviduConfig openviduConfig;
@Autowired
OpenviduBuildInfo openviduBuildConfig;
@Autowired
GeoLocationByIp geoLocationByIp;
@ -292,6 +299,8 @@ public class RpcHandler extends DefaultJsonRpcHandler<JsonObject> {
participant.getPlatform());
}
checkSdkVersionCompliancy(request, participant);
rpcConnection.setSessionId(sessionId);
sessionManager.joinRoom(participant, sessionId, request.getId());
@ -795,36 +804,36 @@ public class RpcHandler extends DefaultJsonRpcHandler<JsonObject> {
return Arrays.asList("*");
}
public static String getStringParam(Request<JsonObject> request, String key) {
public static String getStringParam(Request<JsonObject> request, String key) throws RuntimeException {
if (request.getParams() == null || request.getParams().get(key) == null) {
throw new RuntimeException("Request element '" + key + "' is missing in method '" + request != null
? request.getMethod()
: "[NO REQUEST OBJECT]"
+ "'. CHECK THAT 'openvidu-server' AND 'openvidu-browser' SHARE THE SAME VERSION NUMBER");
+ "'. Check that 'openvidu-server' AND 'openvidu-browser' versions are compatible with each other");
}
return request.getParams().get(key).getAsString();
}
public static int getIntParam(Request<JsonObject> request, String key) {
public static int getIntParam(Request<JsonObject> request, String key) throws RuntimeException {
if (request.getParams() == null || request.getParams().get(key) == null) {
throw new RuntimeException("Request element '" + key + "' is missing in method '" + request.getMethod()
+ "'. CHECK THAT 'openvidu-server' AND 'openvidu-browser' SHARE THE SAME VERSION NUMBER");
+ "'. Check that 'openvidu-server' AND 'openvidu-browser' versions are compatible with each other");
}
return request.getParams().get(key).getAsInt();
}
public static boolean getBooleanParam(Request<JsonObject> request, String key) {
public static boolean getBooleanParam(Request<JsonObject> request, String key) throws RuntimeException {
if (request.getParams() == null || request.getParams().get(key) == null) {
throw new RuntimeException("Request element '" + key + "' is missing in method '" + request.getMethod()
+ "'. CHECK THAT 'openvidu-server' AND 'openvidu-browser' SHARE THE SAME VERSION NUMBER");
+ "'. Check that 'openvidu-server' AND 'openvidu-browser' versions are compatible with each other");
}
return request.getParams().get(key).getAsBoolean();
}
public static JsonElement getParam(Request<JsonObject> request, String key) {
public static JsonElement getParam(Request<JsonObject> request, String key) throws RuntimeException {
if (request.getParams() == null || request.getParams().get(key) == null) {
throw new RuntimeException("Request element '" + key + "' is missing in method '" + request.getMethod()
+ "'. CHECK THAT 'openvidu-server' AND 'openvidu-browser' SHARE THE SAME VERSION NUMBER");
+ "'. Check that 'openvidu-server' AND 'openvidu-browser' versions are compatible with each other");
}
return request.getParams().get(key);
}
@ -879,4 +888,39 @@ public class RpcHandler extends DefaultJsonRpcHandler<JsonObject> {
return senderPublicId;
}
private void checkSdkVersionCompliancy(Request<JsonObject> request, Participant participant) {
// TODO: remove try-catch after release 2.21.0
String clientVersion = null;
try {
clientVersion = getStringParam(request, ProtocolElements.JOINROOM_SDKVERSION_PARAM);
} catch (RuntimeException e) {
// This empty catch is here to support client SDK 2.20.0 with server 2.21.0
// TODO: remove try-catch after release 2.21.0
return;
}
final String serverVersion = openviduBuildConfig.getOpenViduServerVersion();
try {
new VersionComparator().checkVersionCompatibility(clientVersion, serverVersion);
} catch (VersionMismatchException e) {
if (e.isIncompatible()) {
log.error(
"Participant {} with IP {} and platform {} has an incompatible version of openvidu-browser SDK ({}) for this OpenVidu deployment ({}). This may cause the system to malfunction",
participant.getParticipantPublicId(), participant.getLocation().getIp(),
participant.getPlatform(), clientVersion, serverVersion);
log.error(e.getMessage());
log.error(
"openvidu-browser SDK is only compatible with the same version or the immediately following minor version of an OpenVidu deployment");
} else {
DefaultArtifactVersion v = new DefaultArtifactVersion(serverVersion);
log.warn(
"Participant {} with IP {} and platform {} has an older version of openvidu-browser SDK ({}) for this OpenVidu deployment ({}). "
+ "These versions are still compatible with each other, but client SDK must be updated as soon as possible to {}.x. This client using "
+ "openvidu-browser {} will become incompatible with the next release of openvidu-server",
participant.getParticipantPublicId(), participant.getLocation().getIp(),
participant.getPlatform(), clientVersion, serverVersion,
(v.getMajorVersion() + "." + v.getMinorVersion()), clientVersion);
}
}
}
}

View File

@ -0,0 +1,70 @@
package io.openvidu.server.utils;
import java.util.regex.Pattern;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
public class VersionComparator {
// https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
private final String OFFICIAL_SEMVER_REGEX = "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$";
/**
* Checks if client version and server version are compatible with each other
*
* @param clientVersion
* @param serverVersion
* @throws VersionMismatchException if versions do not comply with semver
* format, or if client version and server
* version do not match
*/
public void checkVersionCompatibility(String clientVersion, String serverVersion) throws VersionMismatchException {
checkSemver(clientVersion, "openvidu-browser");
checkSemver(serverVersion, "openvidu-server");
DefaultArtifactVersion comparableClientVersion = new DefaultArtifactVersion(clientVersion);
DefaultArtifactVersion comparableServerVersion = new DefaultArtifactVersion(serverVersion);
if (comparableClientVersion.getMajorVersion() != comparableServerVersion.getMajorVersion()) {
throw new VersionMismatchException(true, "Incompatible major versions of openvidu-browser (\""
+ clientVersion + "\") and openvidu-server (\"" + serverVersion + "\")");
}
checkMinorVersions(comparableClientVersion, comparableServerVersion);
}
private void checkSemver(String version, String artifactName) {
if (!Pattern.compile(OFFICIAL_SEMVER_REGEX).matcher(version).matches()) {
throw new RuntimeException(
"Version \"" + version + "\" of " + artifactName + " does not comply with semver format");
}
}
private void checkMinorVersions(DefaultArtifactVersion clientVersion, DefaultArtifactVersion serverVersion)
throws VersionMismatchException {
int difference = serverVersion.getMinorVersion() - clientVersion.getMinorVersion();
if (difference == 1) {
throw new VersionMismatchException(false, "openvidu-browser \"" + clientVersion + "\" requires update to \""
+ serverVersion.getMajorVersion() + "." + serverVersion.getMinorVersion() + ".x\"");
}
if (difference != 0) {
throw new VersionMismatchException(true, "Incompatible minor versions of openvidu-browser (\""
+ clientVersion + "\") and openvidu-server (\"" + serverVersion + "\")");
}
}
public class VersionMismatchException extends Exception {
private static final long serialVersionUID = 1L;
private boolean incompatible;
public VersionMismatchException(boolean incompatible, String errorMessage) {
super(errorMessage);
this.incompatible = incompatible;
}
public boolean isIncompatible() {
return this.incompatible;
}
}
}

View File

@ -0,0 +1,94 @@
package io.openvidu.server.test.unit;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
import io.openvidu.server.utils.VersionComparator;
import io.openvidu.server.utils.VersionComparator.VersionMismatchException;
public class VersionComparatorTest {
@Test
void versionSyntaxWrongTest() throws VersionMismatchException {
VersionComparator comparator = new VersionComparator();
assertThrows(RuntimeException.class, () -> {
comparator.checkVersionCompatibility("not_a_valid_semver", "2.21.0");
});
assertThrows(RuntimeException.class, () -> {
comparator.checkVersionCompatibility("2.21.0", "not.valid.semver");
});
assertThrows(RuntimeException.class, () -> {
comparator.checkVersionCompatibility("2.21", "2.21");
});
assertThrows(RuntimeException.class, () -> {
comparator.checkVersionCompatibility("2.21.0", "2.21");
});
assertThrows(RuntimeException.class, () -> {
comparator.checkVersionCompatibility("2", "2.21.0");
});
assertThrows(RuntimeException.class, () -> {
comparator.checkVersionCompatibility("", "");
});
}
@Test
void versionsDoMatchTest() throws VersionMismatchException {
VersionComparator comparator = new VersionComparator();
comparator.checkVersionCompatibility("2.21.0", "2.21.0");
comparator.checkVersionCompatibility("2.21.1", "2.21.0");
comparator.checkVersionCompatibility("2.19.20", "2.19.0");
comparator.checkVersionCompatibility("2.21.1-beta", "2.21.0");
comparator.checkVersionCompatibility("3.0.0", "3.0.0-beta4");
}
@Test
void versionsDoNotMatchButAreCompatibleTest() throws VersionMismatchException {
VersionComparator comparator = new VersionComparator();
try {
comparator.checkVersionCompatibility("2.20.0", "2.21.0");
} catch (VersionMismatchException e) {
assertFalse(e.isIncompatible());
}
try {
comparator.checkVersionCompatibility("2.20.5", "2.21.0");
} catch (VersionMismatchException e) {
assertFalse(e.isIncompatible());
}
try {
comparator.checkVersionCompatibility("2.20.5-dev", "2.21.0");
} catch (VersionMismatchException e) {
assertFalse(e.isIncompatible());
}
try {
comparator.checkVersionCompatibility("2.0.0", "2.0.0-dev1");
} catch (VersionMismatchException e) {
assertFalse(e.isIncompatible());
}
}
@Test
void versionsIncompatibleTest() {
VersionComparator comparator = new VersionComparator();
assertThrows(VersionMismatchException.class, () -> {
comparator.checkVersionCompatibility("2.0.0", "3.0.0");
});
assertThrows(VersionMismatchException.class, () -> {
comparator.checkVersionCompatibility("3.0.0", "2.0.0");
});
assertThrows(VersionMismatchException.class, () -> {
comparator.checkVersionCompatibility("2.21.0", "2.23.0");
});
assertThrows(VersionMismatchException.class, () -> {
comparator.checkVersionCompatibility("2.21.0", "2.30.0");
});
assertThrows(VersionMismatchException.class, () -> {
comparator.checkVersionCompatibility("2.22.0-dev1", "2.21.0");
});
assertThrows(VersionMismatchException.class, () -> {
comparator.checkVersionCompatibility("2.22.0", "2.21.0-dev1");
});
}
}

View File

@ -75,6 +75,7 @@
<version.nexus.staging.plugin>1.6.8</version.nexus.staging.plugin>
<version.exec.plugin>3.0.0</version.exec.plugin>
<version.javadoc.plugin>3.2.0</version.javadoc.plugin>
<version.maven.artifact>3.8.3</version.maven.artifact>
<maven.compiler.target>11</maven.compiler.target>
<maven.compiler.source>11</maven.compiler.source>