openvidu-java-client: refactoring to use Java HttpClient. Allow custom client

pull/786/head
pabloFuente 2023-01-27 16:15:20 +01:00
parent 836fa84cd1
commit b78b127447
15 changed files with 929 additions and 618 deletions

View File

@ -63,11 +63,6 @@
</distributionManagement> </distributionManagement>
<dependencies> <dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency> <dependency>
<groupId>com.google.code.gson</groupId> <groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId> <artifactId>gson</artifactId>
@ -106,6 +101,7 @@
<version>${version.javadoc.plugin}</version> <version>${version.javadoc.plugin}</version>
<configuration> <configuration>
<show>public</show> <show>public</show>
<excludePackageNames>io.openvidu.java.client.utils</excludePackageNames>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
@ -142,6 +138,7 @@
<configuration> <configuration>
<show>public</show> <show>public</show>
<javadocExecutable>${java.home}/bin/javadoc</javadocExecutable> <javadocExecutable>${java.home}/bin/javadoc</javadocExecutable>
<excludePackageNames>io.openvidu.java.client.utils</excludePackageNames>
</configuration> </configuration>
<executions> <executions>
<execution> <execution>

View File

@ -17,19 +17,23 @@
package io.openvidu.java.client; package io.openvidu.java.client;
import com.google.gson.JsonObject;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.validator.routines.DomainValidator;
import org.apache.commons.validator.routines.InetAddressValidator;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.Inet6Address; import java.net.Inet6Address;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.*; import java.util.Arrays;
import java.util.Base64;
import java.util.HashSet;
import java.util.Set;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.validator.routines.DomainValidator;
import org.apache.commons.validator.routines.InetAddressValidator;
import com.google.gson.JsonObject;
/** /**
* See * See
@ -42,23 +46,28 @@ public class IceServerProperties {
private String credential; private String credential;
/** /**
* Returns the defined ICE Server url for this {@link IceServerProperties} object. * Returns the defined ICE Server url for this {@link IceServerProperties}
* object.
*/ */
public String getUrl() { public String getUrl() {
return url; return url;
} }
/** /**
* Returns the Username to be used for TURN connections at the defined {@link IceServerProperties#getUrl()} * Returns the Username to be used for TURN connections at the defined
* and {@link IceServerProperties#getCredential()} for this {@link IceServerProperties} object. * {@link IceServerProperties#getUrl()} and
* {@link IceServerProperties#getCredential()} for this
* {@link IceServerProperties} object.
*/ */
public String getUsername() { public String getUsername() {
return username; return username;
} }
/** /**
* Returns the credential to be used for TURN connections at the defined {@link IceServerProperties#getUrl()} * Returns the credential to be used for TURN connections at the defined
* and {@link IceServerProperties#getUsername()} for this {@link IceServerProperties} object. * {@link IceServerProperties#getUrl()} and
* {@link IceServerProperties#getUsername()} for this
* {@link IceServerProperties} object.
*/ */
public String getCredential() { public String getCredential() {
return credential; return credential;
@ -97,11 +106,15 @@ public class IceServerProperties {
private boolean ignoreEmptyUrl = false; private boolean ignoreEmptyUrl = false;
/** /**
* Set the url for the ICE Server you want to use. * Set the url for the ICE Server you want to use. It should follow a valid
* It should follow a valid format: * format:
* <ul> * <ul>
* <li><a href="https://datatracker.ietf.org/doc/html/rfc7065#section-3.1" target="_blank">https://datatracker.ietf.org/doc/html/rfc7065#section-3.1</a></li> * <li><a href="https://datatracker.ietf.org/doc/html/rfc7065#section-3.1"
* <li><a href="https://datatracker.ietf.org/doc/html/rfc7064#section-3.1" target="_blank">https://datatracker.ietf.org/doc/html/rfc7064#section-3.1</a></li> * target=
* "_blank">https://datatracker.ietf.org/doc/html/rfc7065#section-3.1</a></li>
* <li><a href="https://datatracker.ietf.org/doc/html/rfc7064#section-3.1"
* target=
* "_blank">https://datatracker.ietf.org/doc/html/rfc7064#section-3.1</a></li>
* </ul> * </ul>
*/ */
public IceServerProperties.Builder url(String url) { public IceServerProperties.Builder url(String url) {
@ -110,8 +123,8 @@ public class IceServerProperties {
} }
/** /**
* Set a username for the ICE Server you want to use. * Set a username for the ICE Server you want to use. This parameter should be
* This parameter should be defined only for TURN, not for STUN ICE Servers. * defined only for TURN, not for STUN ICE Servers.
*/ */
public IceServerProperties.Builder username(String userName) { public IceServerProperties.Builder username(String userName) {
this.username = userName; this.username = userName;
@ -119,8 +132,8 @@ public class IceServerProperties {
} }
/** /**
* Set a credential for the ICE Server you want to use. * Set a credential for the ICE Server you want to use. This parameter should be
* This parameter should be defined only for TURN, not for STUN ICE Servers. * defined only for TURN, not for STUN ICE Servers.
*/ */
public IceServerProperties.Builder credential(String credential) { public IceServerProperties.Builder credential(String credential) {
this.credential = credential; this.credential = credential;
@ -130,11 +143,17 @@ public class IceServerProperties {
/** /**
* Secret for TURN authentication based on: * Secret for TURN authentication based on:
* <ul> * <ul>
* <li><a href="https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00" target="_blank">https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00</a></li> * <li><a href="https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00"
* <li><a href="https://www.ietf.org/proceedings/87/slides/slides-87-behave-10.pdf" target="_blank">https://www.ietf.org/proceedings/87/slides/slides-87-behave-10.pdf</a></li> * target=
* "_blank">https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00</a></li>
* <li><a href=
* "https://www.ietf.org/proceedings/87/slides/slides-87-behave-10.pdf" target=
* "_blank">https://www.ietf.org/proceedings/87/slides/slides-87-behave-10.pdf</a></li>
* </ul> * </ul>
* This will generate credentials valid for 24 hours which is the recommended value. You need to setup in your TURN service this same secret value * This will generate credentials valid for 24 hours which is the recommended
* which uses HMAC SHA1 as encryption algorithm. A TURN implementation which by default uses this is COTURN with static-auth-secret parameter. * value. You need to setup in your TURN service this same secret value which
* uses HMAC SHA1 as encryption algorithm. A TURN implementation which by
* default uses this is COTURN with static-auth-secret parameter.
*/ */
public IceServerProperties.Builder staticAuthSecret(String staticAuthSecret) { public IceServerProperties.Builder staticAuthSecret(String staticAuthSecret) {
this.staticAuthSecret = staticAuthSecret; this.staticAuthSecret = staticAuthSecret;
@ -147,19 +166,24 @@ public class IceServerProperties {
} }
public IceServerProperties.Builder clone() { public IceServerProperties.Builder clone() {
return new Builder().url(this.url) return new Builder().url(this.url).username(this.username).credential(this.credential)
.username(this.username)
.credential(this.credential)
.staticAuthSecret(this.staticAuthSecret); .staticAuthSecret(this.staticAuthSecret);
} }
/** /**
* Builder for {@link io.openvidu.java.client.RecordingProperties} * Builder for {@link io.openvidu.java.client.RecordingProperties}
*
* @throws IllegalArgumentException if the defined properties does not follows * @throws IllegalArgumentException if the defined properties does not follows
* common STUN/TURN RFCs: * common STUN/TURN RFCs:
* <ul> * <ul>
* <li><a href="https://datatracker.ietf.org/doc/html/rfc7065#section-3.1" target="_blank">https://datatracker.ietf.org/doc/html/rfc7065#section-3.1</a></li> * <li><a href=
* <li><a href="https://datatracker.ietf.org/doc/html/rfc7064#section-3.1" target="_blank">https://datatracker.ietf.org/doc/html/rfc7064#section-3.1</a></li> * "https://datatracker.ietf.org/doc/html/rfc7065#section-3.1"
* target=
* "_blank">https://datatracker.ietf.org/doc/html/rfc7065#section-3.1</a></li>
* <li><a href=
* "https://datatracker.ietf.org/doc/html/rfc7064#section-3.1"
* target=
* "_blank">https://datatracker.ietf.org/doc/html/rfc7064#section-3.1</a></li>
* </ul> * </ul>
*/ */
public IceServerProperties build() throws IllegalArgumentException { public IceServerProperties build() throws IllegalArgumentException {
@ -172,7 +196,8 @@ public class IceServerProperties {
throw new IllegalArgumentException("Error while generating credentials: " + e.getMessage()); throw new IllegalArgumentException("Error while generating credentials: " + e.getMessage());
} }
} else { } else {
throw new IllegalArgumentException("ignoreEmptyUrl=true can only be used with staticAuthSecret defined"); throw new IllegalArgumentException(
"ignoreEmptyUrl=true can only be used with staticAuthSecret defined");
} }
} }
if (this.url == null) { if (this.url == null) {
@ -182,7 +207,8 @@ public class IceServerProperties {
if (this.url.startsWith("turn")) { if (this.url.startsWith("turn")) {
if (this.staticAuthSecret != null) { if (this.staticAuthSecret != null) {
if (this.username != null || this.credential != null) { if (this.username != null || this.credential != null) {
throw new IllegalArgumentException("You can't define username or credential if staticAuthSecret is defined"); throw new IllegalArgumentException(
"You can't define username or credential if staticAuthSecret is defined");
} }
try { try {
this.generateTURNCredentials(); this.generateTURNCredentials();
@ -207,20 +233,14 @@ public class IceServerProperties {
final String UDP_TRANSPORT_SUFFIX = "?transport=udp"; final String UDP_TRANSPORT_SUFFIX = "?transport=udp";
// Protocols which accepts transport=tcp and transport=udp // Protocols which accepts transport=tcp and transport=udp
final Set<String> TURN_PROTOCOLS = new HashSet<>(Arrays.asList( final Set<String> TURN_PROTOCOLS = new HashSet<>(Arrays.asList("turn", "turns"));
"turn", final Set<String> STUN_PROTOCOLS = new HashSet<>(Arrays.asList("stun", "stuns"));
"turns"
));
final Set<String> STUN_PROTOCOLS = new HashSet<>(Arrays.asList(
"stun",
"stuns"
));
// Fails if no colons // Fails if no colons
int firstColonPos = uri.indexOf(':'); int firstColonPos = uri.indexOf(':');
if (firstColonPos == -1) { if (firstColonPos == -1) {
throw new IllegalArgumentException("Not a valid TURN/STUN uri provided. " + throw new IllegalArgumentException(
"No colons found in: '" + uri + "'"); "Not a valid TURN/STUN uri provided. " + "No colons found in: '" + uri + "'");
} }
// Get protocol and check // Get protocol and check
@ -238,11 +258,13 @@ public class IceServerProperties {
// Only Turn uses transport arg // Only Turn uses transport arg
String rawTransportType = uri.substring(qmarkPos); String rawTransportType = uri.substring(qmarkPos);
hostAndPort = uri.substring(firstColonPos + 1, qmarkPos); hostAndPort = uri.substring(firstColonPos + 1, qmarkPos);
if (!TCP_TRANSPORT_SUFFIX.equals(rawTransportType) && !UDP_TRANSPORT_SUFFIX.equals(rawTransportType)) { if (!TCP_TRANSPORT_SUFFIX.equals(rawTransportType)
// If other argument rather than transport is specified, it is a wrong query for a STUN/TURN uri && !UDP_TRANSPORT_SUFFIX.equals(rawTransportType)) {
throw new IllegalArgumentException("Wrong value specified in STUN/TURN uri: '" // If other argument rather than transport is specified, it is a wrong query for
+ uri + "'. " + "Unique valid arguments after '?' are '" // a STUN/TURN uri
+ TCP_TRANSPORT_SUFFIX + "' or '" + UDP_TRANSPORT_SUFFIX); throw new IllegalArgumentException("Wrong value specified in STUN/TURN uri: '" + uri + "'. "
+ "Unique valid arguments after '?' are '" + TCP_TRANSPORT_SUFFIX + "' or '"
+ UDP_TRANSPORT_SUFFIX);
} }
} else { } else {
throw new IllegalArgumentException("STUN uri can't have any '?' query param"); throw new IllegalArgumentException("STUN uri can't have any '?' query param");
@ -263,13 +285,14 @@ public class IceServerProperties {
// If Ipv4 and port defined // If Ipv4 and port defined
String[] splittedHostAndPort = hostAndPort.split(":"); String[] splittedHostAndPort = hostAndPort.split(":");
if (splittedHostAndPort.length != 2) { if (splittedHostAndPort.length != 2) {
throw new IllegalArgumentException("Host or port are not correctly " + throw new IllegalArgumentException(
"defined in STUN/TURN uri: '" + uri + "'"); "Host or port are not correctly " + "defined in STUN/TURN uri: '" + uri + "'");
} }
String host = splittedHostAndPort[0]; String host = splittedHostAndPort[0];
String port = splittedHostAndPort[1]; String port = splittedHostAndPort[1];
// Check if host is defined. Valid Host (Domain or IP) will be done at server side // Check if host is defined. Valid Host (Domain or IP) will be done at server
// side
checkHostAndPort(uri, host, port); checkHostAndPort(uri, host, port);
} else { } else {
// If portColon is found and Ipv6 // If portColon is found and Ipv6
@ -277,7 +300,8 @@ public class IceServerProperties {
String auxPort = hostAndPort.substring(endIpv6Index + 1); String auxPort = hostAndPort.substring(endIpv6Index + 1);
if (auxPort.startsWith(":")) { if (auxPort.startsWith(":")) {
if (auxPort.length() == 1) { if (auxPort.length() == 1) {
throw new IllegalArgumentException("Host or port are not correctly defined in STUN/TURN uri: " + uri); throw new IllegalArgumentException(
"Host or port are not correctly defined in STUN/TURN uri: " + uri);
} }
// If port is defined // If port is defined
// Get port without colon and check host and port // Get port without colon and check host and port
@ -286,7 +310,8 @@ public class IceServerProperties {
checkHostAndPort(uri, host, port); checkHostAndPort(uri, host, port);
} else if (auxPort.length() > 0) { } else if (auxPort.length() > 0) {
// If auxPort = 0, no port is defined // If auxPort = 0, no port is defined
throw new IllegalArgumentException("Port is not specified correctly after IPv6 in uri: '" + uri + "'"); throw new IllegalArgumentException(
"Port is not specified correctly after IPv6 in uri: '" + uri + "'");
} }
} }
} else { } else {
@ -311,7 +336,8 @@ public class IceServerProperties {
Inet6Address.getByName(host).getHostAddress(); Inet6Address.getByName(host).getHostAddress();
return; return;
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
throw new IllegalArgumentException("Is not a valid Internet Address (IP or Domain Name): '" + host + "'"); throw new IllegalArgumentException(
"Is not a valid Internet Address (IP or Domain Name): '" + host + "'");
} }
} }
@ -323,7 +349,8 @@ public class IceServerProperties {
try { try {
int parsedPort = Integer.parseInt(port); int parsedPort = Integer.parseInt(port);
if (parsedPort <= 0 || parsedPort > 65535) { if (parsedPort <= 0 || parsedPort > 65535) {
throw new IllegalArgumentException("The port defined in '" + uri + "' is not a valid port number (0-65535)"); throw new IllegalArgumentException(
"The port defined in '" + uri + "' is not a valid port number (0-65535)");
} }
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
throw new IllegalArgumentException("The port defined in '" + uri + "' is not a number (0-65535)"); throw new IllegalArgumentException("The port defined in '" + uri + "' is not a number (0-65535)");
@ -335,19 +362,17 @@ public class IceServerProperties {
this.checkPort(uri, port); this.checkPort(uri, port);
} }
private void generateTURNCredentials() throws NoSuchAlgorithmException, InvalidKeyException { private void generateTURNCredentials() throws NoSuchAlgorithmException, InvalidKeyException {
// 1. Generate random username // 1. Generate random username
char[] ALPHANUMERIC =("abcdefghijklmnopqrstuvwxyzABCDEFGHIJK" + char[] ALPHANUMERIC = ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJK" + "LMNOPQRSTUVWXYZ0123456789").toCharArray();
"LMNOPQRSTUVWXYZ0123456789").toCharArray();
int MAX_LENGTH = 8; int MAX_LENGTH = 8;
StringBuilder randomUsername = new StringBuilder(); StringBuilder randomUsername = new StringBuilder();
for(int i =0; i < MAX_LENGTH; i++) { for (int i = 0; i < MAX_LENGTH; i++) {
int index = new SecureRandom().nextInt(ALPHANUMERIC.length); int index = new SecureRandom().nextInt(ALPHANUMERIC.length);
randomUsername.append(ALPHANUMERIC[index]); randomUsername.append(ALPHANUMERIC[index]);
} }
// 2. Get unix timestamp adding 24 hours to define max credential valid time // 2. Get unix timestamp adding 24 hours to define max credential valid time
String unixTimestamp = Long.toString((System.currentTimeMillis() / 1000) + 24*3600); String unixTimestamp = Long.toString((System.currentTimeMillis() / 1000) + 24 * 3600);
// 3. Generate TURN username // 3. Generate TURN username
String username = unixTimestamp + ":" + randomUsername; String username = unixTimestamp + ":" + randomUsername;
@ -356,7 +381,7 @@ public class IceServerProperties {
SecretKeySpec signingKey = new SecretKeySpec(staticAuthSecret.getBytes(), "HmacSHA1"); SecretKeySpec signingKey = new SecretKeySpec(staticAuthSecret.getBytes(), "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1"); Mac mac = Mac.getInstance("HmacSHA1");
mac.init(signingKey); mac.init(signingKey);
String credential = new String(Base64.encodeBase64(mac.doFinal(username.getBytes()))); String credential = new String(Base64.getEncoder().encode(mac.doFinal(username.getBytes())));
// Set credentials in builder // Set credentials in builder
this.username = username; this.username = username;

View File

@ -18,55 +18,49 @@
package io.openvidu.java.client; package io.openvidu.java.client;
import java.io.IOException; import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Builder;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.KeyManagementException; import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext; import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedTrustManager;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.ParseException;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
public class OpenVidu { public class OpenVidu {
private static final Logger log = LoggerFactory.getLogger(OpenVidu.class); private static final Logger log = LoggerFactory.getLogger(OpenVidu.class);
private String secret;
protected String hostname; protected String hostname;
protected HttpClient httpClient; protected HttpClient httpClient;
protected Map<String, Session> activeSessions = new ConcurrentHashMap<>(); protected Map<String, Session> activeSessions = new ConcurrentHashMap<>();
protected long requestTimeout = 30000;
protected Map<String, String> headers = new HashMap<>();
protected final static String API_PATH = "openvidu/api"; protected final static String API_PATH = "openvidu/api";
protected final static String API_SESSIONS = API_PATH + "/sessions"; protected final static String API_SESSIONS = API_PATH + "/sessions";
@ -75,49 +69,149 @@ public class OpenVidu {
protected final static String API_RECORDINGS_START = API_RECORDINGS + "/start"; protected final static String API_RECORDINGS_START = API_RECORDINGS + "/start";
protected final static String API_RECORDINGS_STOP = API_RECORDINGS + "/stop"; protected final static String API_RECORDINGS_STOP = API_RECORDINGS + "/stop";
private String defaultBasicAuth;
/** /**
* @param hostname URL where your instance of OpenVidu Server is up an running. * @param hostname URL where your OpenVidu deployment is up an running. It must
* It must be the full URL (e.g. * be the full URL (e.g. <code>https://12.34.56.78:1234/</code>)
* <code>https://12.34.56.78:1234/</code>)
* *
* @param secret Secret used on OpenVidu Server initialization * @param secret Secret configured in your OpenVidu deployment
*/ */
public OpenVidu(String hostname, String secret) { public OpenVidu(String hostname, String secret) {
this.hostname = hostname; testHostname(hostname);
setDefaultBasicAuth(secret);
this.hostname = hostname;
if (!this.hostname.endsWith("/")) { if (!this.hostname.endsWith("/")) {
this.hostname += "/"; this.hostname += "/";
} }
this.secret = secret;
TrustStrategy trustStrategy = new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
};
CredentialsProvider provider = new BasicCredentialsProvider();
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("OPENVIDUAPP", this.secret);
provider.setCredentials(AuthScope.ANY, credentials);
SSLContext sslContext; SSLContext sslContext;
try { try {
sslContext = new SSLContextBuilder().loadTrustMaterial(null, trustStrategy).build(); sslContext = SSLContext.getInstance("TLS");
} catch (KeyManagementException | NoSuchAlgorithmException | KeyStoreException e) { sslContext.init(null, new TrustManager[] { new X509ExtendedTrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(final X509Certificate[] a_certificates, final String a_auth_type) {
}
public void checkServerTrusted(final X509Certificate[] a_certificates, final String a_auth_type) {
}
public void checkClientTrusted(final X509Certificate[] a_certificates, final String a_auth_type,
final Socket a_socket) {
}
public void checkServerTrusted(final X509Certificate[] a_certificates, final String a_auth_type,
final Socket a_socket) {
}
public void checkClientTrusted(final X509Certificate[] a_certificates, final String a_auth_type,
final SSLEngine a_engine) {
}
public void checkServerTrusted(final X509Certificate[] a_certificates, final String a_auth_type,
final SSLEngine a_engine) {
}
} }, null);
} catch (KeyManagementException | NoSuchAlgorithmException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
RequestConfig.Builder requestBuilder = RequestConfig.custom(); Builder b = HttpClient.newBuilder();
requestBuilder = requestBuilder.setConnectTimeout(30000); b.sslContext(sslContext);
requestBuilder = requestBuilder.setConnectionRequestTimeout(30000); b.connectTimeout(Duration.ofSeconds(30));
this.httpClient = b.build();
}
this.httpClient = HttpClientBuilder.create().setDefaultRequestConfig(requestBuilder.build()) /**
.setConnectionTimeToLive(30, TimeUnit.SECONDS).setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) * @param hostname URL where your OpenVidu deployment is up an running. It
.setSSLContext(sslContext).setDefaultCredentialsProvider(provider).build(); * must be the full URL (e.g.
* <code>https://12.34.56.78:1234/</code>)
* @param httpClient Object of class <a target="_blank" href=
* "https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpClient.html">java.net.http.HttpClient</a>.
* This overrides the internal HTTP client in use. This method
* allows you to custom configure the HTTP client to your
* needs. This may be interesting for many reasons, including:
* <ul>
* <li>Adding proxy configuration</li>
* <li>Customizing the SSLContext</li>
* <li>Modifying the connection timeout</li>
* <li>Adding a cookie handler</li>
* </ul>
*/
public OpenVidu(String hostname, String secret, HttpClient httpClient) {
testHostname(hostname);
setDefaultBasicAuth(secret);
this.hostname = hostname;
if (!this.hostname.endsWith("/")) {
this.hostname += "/";
}
Builder b = HttpClient.newBuilder();
if (httpClient.authenticator().isPresent()) {
log.warn(
"The provided HttpClient contains a custom java.net.Authenticator. OpenVidu deployments require all HTTP requests to have an Authorization header with Basic Auth, with username \"OPENVIDUAPP\" and password your OpenVidu deployment's secret (configuration parameter OPENVIDU_SECRET). The default Authenticator adds this header. Make sure that your custom Authenticator does so as well or that you add it explicitly via OpenVidu#setRequestHeaders, or you will receive 401 responses");
b.authenticator(httpClient.authenticator().get());
}
if (httpClient.connectTimeout().isPresent()) {
b.connectTimeout(httpClient.connectTimeout().get());
}
if (httpClient.cookieHandler().isPresent()) {
b.cookieHandler(httpClient.cookieHandler().get());
}
if (httpClient.executor().isPresent()) {
b.executor(httpClient.executor().get());
}
if (httpClient.proxy().isPresent()) {
b.proxy(httpClient.proxy().get());
}
b.followRedirects(httpClient.followRedirects());
b.sslContext(httpClient.sslContext());
b.sslParameters(httpClient.sslParameters());
b.version(httpClient.version());
this.httpClient = b.build();
}
/**
* Returns the current request timeout configured for all requests.
*/
public long getRequestTimeout() {
return this.requestTimeout;
}
/**
* Sets the request timeout for all HTTP requests. This is the same as setting
* the <a target="_blank" href=
* "https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpRequest.html#timeout()">HttpRequest.timeout()</a>
* property for all requests. The default value is 30000 ms.
*
* @param requestTimeout Timeout in milliseconds
*/
public void setRequestTimeout(long requestTimeout) {
this.requestTimeout = requestTimeout;
}
/**
* Returns the current collection of custom headers configured for all requests.
*/
public Map<String, String> getRequestHeaders() {
return this.headers;
}
/**
* Sets custom HTTP headers for all requests. Will override any previous value.
*
* @param headers Header names and values
*/
public void setRequestHeaders(Map<String, String> headers) {
this.headers = headers;
} }
/** /**
@ -183,23 +277,20 @@ public class OpenVidu {
public Recording startRecording(String sessionId, RecordingProperties properties) public Recording startRecording(String sessionId, RecordingProperties properties)
throws OpenViduJavaClientException, OpenViduHttpException { throws OpenViduJavaClientException, OpenViduHttpException {
HttpPost request = new HttpPost(this.hostname + API_RECORDINGS_START); try {
JsonObject json = properties.toJson(); JsonObject json = properties.toJson();
json.addProperty("session", sessionId); json.addProperty("session", sessionId);
StringEntity params = new StringEntity(json.toString(), "UTF-8"); HttpRequest request = addHeaders(
HttpRequest.newBuilder().POST(HttpRequest.BodyPublishers.ofString(json.toString()))
.uri(new URI(this.hostname + API_RECORDINGS_START))
.setHeader("Content-Type", "application/json").timeout(Duration.ofMillis(requestTimeout)))
.build();
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json"); HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
request.setEntity(params);
HttpResponse response = null; if ((response.statusCode() == HttpURLConnection.HTTP_OK)) {
try { Recording r = new Recording(Utils.httpResponseToJson(response));
response = this.httpClient.execute(request);
int statusCode = response.getStatusLine().getStatusCode();
if ((statusCode == org.apache.http.HttpStatus.SC_OK)) {
Recording r = new Recording(httpResponseToJson(response));
Session activeSession = this.activeSessions.get(r.getSessionId()); Session activeSession = this.activeSessions.get(r.getSessionId());
if (activeSession != null) { if (activeSession != null) {
activeSession.setIsBeingRecorded(true); activeSession.setIsBeingRecorded(true);
@ -210,15 +301,11 @@ public class OpenVidu {
} }
return r; return r;
} else { } else {
throw new OpenViduHttpException(statusCode); throw new OpenViduHttpException(response.statusCode());
} }
} catch (IOException e) { } catch (URISyntaxException | IOException | InterruptedException e) {
throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
} finally {
if (response != null) {
EntityUtils.consumeQuietly(response.getEntity());
}
} }
} }
@ -288,14 +375,16 @@ public class OpenVidu {
* API</a>) * API</a>)
*/ */
public Recording stopRecording(String recordingId) throws OpenViduJavaClientException, OpenViduHttpException { public Recording stopRecording(String recordingId) throws OpenViduJavaClientException, OpenViduHttpException {
HttpPost request = new HttpPost(this.hostname + API_RECORDINGS_STOP + "/" + recordingId);
HttpResponse response = null;
try {
response = this.httpClient.execute(request);
int statusCode = response.getStatusLine().getStatusCode(); try {
if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { HttpRequest request = addHeaders(HttpRequest.newBuilder().POST(HttpRequest.BodyPublishers.noBody())
Recording r = new Recording(httpResponseToJson(response)); .uri(new URI(this.hostname + API_RECORDINGS_STOP + "/" + recordingId))
.timeout(Duration.ofMillis(requestTimeout))).build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == HttpURLConnection.HTTP_OK) {
Recording r = new Recording(Utils.httpResponseToJson(response));
Session activeSession = this.activeSessions.get(r.getSessionId()); Session activeSession = this.activeSessions.get(r.getSessionId());
if (activeSession != null) { if (activeSession != null) {
activeSession.setIsBeingRecorded(false); activeSession.setIsBeingRecorded(false);
@ -306,14 +395,10 @@ public class OpenVidu {
} }
return r; return r;
} else { } else {
throw new OpenViduHttpException(statusCode); throw new OpenViduHttpException(response.statusCode());
} }
} catch (IOException e) { } catch (URISyntaxException | IOException | InterruptedException e) {
throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
} finally {
if (response != null) {
EntityUtils.consumeQuietly(response.getEntity());
}
} }
} }
@ -331,24 +416,22 @@ public class OpenVidu {
* API</a>) * API</a>)
*/ */
public Recording getRecording(String recordingId) throws OpenViduJavaClientException, OpenViduHttpException { public Recording getRecording(String recordingId) throws OpenViduJavaClientException, OpenViduHttpException {
HttpGet request = new HttpGet(this.hostname + API_RECORDINGS + "/" + recordingId);
HttpResponse response = null;
try { try {
response = this.httpClient.execute(request); HttpRequest request = addHeaders(
HttpRequest.newBuilder().GET().uri(new URI(this.hostname + API_RECORDINGS + "/" + recordingId))
.timeout(Duration.ofMillis(requestTimeout)))
.build();
int statusCode = response.getStatusLine().getStatusCode(); HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if ((statusCode == org.apache.http.HttpStatus.SC_OK)) {
return new Recording(httpResponseToJson(response)); if (response.statusCode() == HttpURLConnection.HTTP_OK) {
return new Recording(Utils.httpResponseToJson(response));
} else { } else {
throw new OpenViduHttpException(statusCode); throw new OpenViduHttpException(response.statusCode());
} }
} catch (URISyntaxException | IOException | InterruptedException e) {
} catch (IOException e) {
throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
} finally {
if (response != null) {
EntityUtils.consumeQuietly(response.getEntity());
}
} }
} }
@ -361,30 +444,26 @@ public class OpenVidu {
* @throws OpenViduHttpException * @throws OpenViduHttpException
*/ */
public List<Recording> listRecordings() throws OpenViduJavaClientException, OpenViduHttpException { public List<Recording> listRecordings() throws OpenViduJavaClientException, OpenViduHttpException {
HttpGet request = new HttpGet(this.hostname + API_RECORDINGS);
HttpResponse response = null;
try {
response = this.httpClient.execute(request);
int statusCode = response.getStatusLine().getStatusCode(); try {
if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { HttpRequest request = addHeaders(HttpRequest.newBuilder().GET().uri(new URI(this.hostname + API_RECORDINGS))
.timeout(Duration.ofMillis(requestTimeout))).build();
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == HttpURLConnection.HTTP_OK) {
List<Recording> recordings = new ArrayList<>(); List<Recording> recordings = new ArrayList<>();
JsonObject json = httpResponseToJson(response); JsonObject json = Utils.httpResponseToJson(response);
JsonArray array = json.get("items").getAsJsonArray(); JsonArray array = json.get("items").getAsJsonArray();
array.forEach(item -> { array.forEach(item -> {
recordings.add(new Recording(item.getAsJsonObject())); recordings.add(new Recording(item.getAsJsonObject()));
}); });
return recordings; return recordings;
} else { } else {
throw new OpenViduHttpException(statusCode); throw new OpenViduHttpException(response.statusCode());
} }
} catch (URISyntaxException | IOException | InterruptedException e) {
} catch (IOException e) {
throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
} finally {
if (response != null) {
EntityUtils.consumeQuietly(response.getEntity());
}
} }
} }
@ -405,22 +484,20 @@ public class OpenVidu {
* API</a>) * API</a>)
*/ */
public void deleteRecording(String recordingId) throws OpenViduJavaClientException, OpenViduHttpException { public void deleteRecording(String recordingId) throws OpenViduJavaClientException, OpenViduHttpException {
HttpDelete request = new HttpDelete(this.hostname + API_RECORDINGS + "/" + recordingId);
HttpResponse response = null;
try { try {
response = this.httpClient.execute(request); HttpRequest request = addHeaders(
HttpRequest.newBuilder().DELETE().uri(new URI(this.hostname + API_RECORDINGS + "/" + recordingId))
.timeout(Duration.ofMillis(requestTimeout)))
.build();
int statusCode = response.getStatusLine().getStatusCode(); HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if (!(statusCode == org.apache.http.HttpStatus.SC_NO_CONTENT)) {
throw new OpenViduHttpException(statusCode); if (response.statusCode() != HttpURLConnection.HTTP_NO_CONTENT) {
throw new OpenViduHttpException(response.statusCode());
} }
} catch (URISyntaxException | IOException | InterruptedException e) {
} catch (IOException e) {
throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
} finally {
if (response != null) {
EntityUtils.consumeQuietly(response.getEntity());
}
} }
} }
@ -488,16 +565,17 @@ public class OpenVidu {
* @throws OpenViduJavaClientException * @throws OpenViduJavaClientException
*/ */
public boolean fetch() throws OpenViduJavaClientException, OpenViduHttpException { public boolean fetch() throws OpenViduJavaClientException, OpenViduHttpException {
HttpGet request = new HttpGet(this.hostname + API_SESSIONS + "?pendingConnections=true");
HttpResponse response = null;
try { try {
response = this.httpClient.execute(request); HttpRequest request = addHeaders(HttpRequest.newBuilder().GET()
.uri(new URI(this.hostname + API_SESSIONS + "?pendingConnections=true"))
.timeout(Duration.ofMillis(requestTimeout))).build();
int statusCode = response.getStatusLine().getStatusCode(); HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
if ((statusCode == org.apache.http.HttpStatus.SC_OK)) {
JsonObject jsonSessions = httpResponseToJson(response); if (response.statusCode() == HttpURLConnection.HTTP_OK) {
JsonObject jsonSessions = Utils.httpResponseToJson(response);
JsonArray jsonArraySessions = jsonSessions.get("content").getAsJsonArray(); JsonArray jsonArraySessions = jsonSessions.get("content").getAsJsonArray();
// Boolean to store if any Session has changed // Boolean to store if any Session has changed
@ -546,26 +624,35 @@ public class OpenVidu {
return hasChanged[0]; return hasChanged[0];
} else { } else {
throw new OpenViduHttpException(statusCode); throw new OpenViduHttpException(response.statusCode());
} }
} catch (IOException e) { } catch (URISyntaxException | IOException | InterruptedException e) {
throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
} finally {
if (response != null) {
EntityUtils.consumeQuietly(response.getEntity());
}
} }
} }
private JsonObject httpResponseToJson(HttpResponse response) throws OpenViduJavaClientException { protected HttpRequest.Builder addHeaders(HttpRequest.Builder builder) {
// HTTP header names are case insensitive
Map<String, String> headersLowerCase = new HashMap<>();
this.headers.forEach((k, v) -> headersLowerCase.put(k.toLowerCase(), v));
if (!headersLowerCase.containsKey("authorization")) {
// There is no custom Authorization header. Add default Basic Auth for OpenVidu
headersLowerCase.put("authorization", this.defaultBasicAuth);
}
headersLowerCase.forEach((k, v) -> builder.setHeader(k, v));
return builder;
}
private void testHostname(String hostnameStr) {
try { try {
JsonObject json = new Gson().fromJson(EntityUtils.toString(response.getEntity(), "UTF-8"), new URL(hostnameStr);
JsonObject.class); } catch (MalformedURLException e) {
return json; throw new RuntimeException("The hostname \"" + hostnameStr + "\" is not a valid URL: " + e.getMessage());
} catch (JsonSyntaxException | ParseException | IOException e) {
throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
} }
} }
private void setDefaultBasicAuth(String secret) {
this.defaultBasicAuth = "Basic " + Base64.getEncoder().encodeToString(("OPENVIDUAPP:" + secret).getBytes());
}
} }

View File

@ -238,7 +238,7 @@ public class Recording {
* URL of the recording. You can access the file from there. It is * URL of the recording. You can access the file from there. It is
* <code>null</code> until recording reaches "ready" or "failed" status. If * <code>null</code> until recording reaches "ready" or "failed" status. If
* <a href="https://docs.openvidu.io/en/stable/reference-docs/openvidu-config/"> * <a href="https://docs.openvidu.io/en/stable/reference-docs/openvidu-config/">
* OpenVidu Server configuration </a> property * OpenVidu configuration </a> property
* <code>OPENVIDU_RECORDING_PUBLIC_ACCESS</code> is false, this path will be * <code>OPENVIDU_RECORDING_PUBLIC_ACCESS</code> is false, this path will be
* secured with OpenVidu credentials * secured with OpenVidu credentials
*/ */

View File

@ -22,7 +22,6 @@ import java.util.Map;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import io.openvidu.java.client.Recording.OutputMode; import io.openvidu.java.client.Recording.OutputMode;
import io.openvidu.java.client.utils.FormatChecker;
/** /**
* See * See
@ -559,7 +558,7 @@ public class RecordingProperties {
} }
if (nameParam != null && !nameParam.isEmpty()) { if (nameParam != null && !nameParam.isEmpty()) {
if (!FormatChecker.isValidRecordingName(nameParam)) { if (!Utils.isValidRecordingName(nameParam)) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Parameter 'name' is wrong. Must be an alphanumeric string [a-zA-Z0-9_-~]+"); "Parameter 'name' is wrong. Must be an alphanumeric string [a-zA-Z0-9_-~]+");
} }
@ -620,7 +619,7 @@ public class RecordingProperties {
} }
if (resolutionParam != null) { if (resolutionParam != null) {
if (!FormatChecker.isAcceptableRecordingResolution(resolutionParam)) { if (!Utils.isAcceptableRecordingResolution(resolutionParam)) {
throw new IllegalStateException( throw new IllegalStateException(
"Wrong 'resolution' parameter. Acceptable values from 100 to 1999 for both width and height"); "Wrong 'resolution' parameter. Acceptable values from 100 to 1999 for both width and height");
} }
@ -630,7 +629,7 @@ public class RecordingProperties {
} }
if (frameRateParam != null) { if (frameRateParam != null) {
if (!FormatChecker.isAcceptableRecordingFrameRate(frameRateParam.intValue())) { if (!Utils.isAcceptableRecordingFrameRate(frameRateParam.intValue())) {
throw new IllegalStateException( throw new IllegalStateException(
"Wrong 'frameRate' parameter. Acceptable values are within range [1,120]"); "Wrong 'frameRate' parameter. Acceptable values are within range [1,120]");
} }
@ -640,7 +639,7 @@ public class RecordingProperties {
} }
if (shmSizeParam != null) { if (shmSizeParam != null) {
if (!FormatChecker.isAcceptableRecordingShmSize(shmSizeParam)) { if (!Utils.isAcceptableRecordingShmSize(shmSizeParam)) {
throw new IllegalStateException("Wrong 'shmSize' parameter. Must be 134217728 (128 MB) minimum"); throw new IllegalStateException("Wrong 'shmSize' parameter. Must be 134217728 (128 MB) minimum");
} }
shmSizeFinal = shmSizeParam; shmSizeFinal = shmSizeParam;

View File

@ -18,6 +18,12 @@
package io.openvidu.java.client; package io.openvidu.java.client;
import java.io.IOException; import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -25,14 +31,6 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPatch;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -40,7 +38,6 @@ import com.google.gson.Gson;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
public class Session { public class Session {
@ -116,35 +113,34 @@ public class Session {
*/ */
@Deprecated @Deprecated
public String generateToken(TokenOptions tokenOptions) throws OpenViduJavaClientException, OpenViduHttpException { public String generateToken(TokenOptions tokenOptions) throws OpenViduJavaClientException, OpenViduHttpException {
if (!this.hasSessionId()) { if (!this.hasSessionId()) {
this.getSessionId(); this.getSessionId();
} }
HttpPost request = new HttpPost(this.openVidu.hostname + OpenVidu.API_TOKENS);
StringEntity params = new StringEntity(tokenOptions.toJsonObject(sessionId).toString(), "UTF-8");
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
request.setEntity(params);
HttpResponse response = null;
try { try {
response = this.openVidu.httpClient.execute(request); JsonObject json = tokenOptions.toJsonObject(sessionId);
int statusCode = response.getStatusLine().getStatusCode(); HttpRequest request = this.openVidu
if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { .addHeaders(HttpRequest.newBuilder().POST(HttpRequest.BodyPublishers.ofString(json.toString()))
String token = httpResponseToJson(response).get("id").getAsString(); .uri(new URI(this.openVidu.hostname + OpenVidu.API_TOKENS))
.setHeader("Content-Type", "application/json")
.timeout(Duration.ofMillis(this.openVidu.requestTimeout)))
.build();
HttpResponse<String> response = this.openVidu.httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == HttpURLConnection.HTTP_OK) {
String token = Utils.httpResponseToJson(response).get("id").getAsString();
log.info("Returning a TOKEN: {}", token); log.info("Returning a TOKEN: {}", token);
return token; return token;
} else { } else {
throw new OpenViduHttpException(statusCode); throw new OpenViduHttpException(response.statusCode());
} }
} catch (IOException e) { } catch (URISyntaxException | IOException | InterruptedException e) {
throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
} finally {
if (response != null) {
EntityUtils.consumeQuietly(response.getEntity());
}
} }
} }
@ -179,36 +175,33 @@ public class Session {
*/ */
public Connection createConnection(ConnectionProperties connectionProperties) public Connection createConnection(ConnectionProperties connectionProperties)
throws OpenViduJavaClientException, OpenViduHttpException { throws OpenViduJavaClientException, OpenViduHttpException {
if (!this.hasSessionId()) { if (!this.hasSessionId()) {
this.getSessionId(); this.getSessionId();
} }
HttpPost request = new HttpPost(
this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/connection");
StringEntity params = new StringEntity(connectionProperties.toJson(sessionId).toString(), "UTF-8");
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
request.setEntity(params);
HttpResponse response = null;
try { try {
response = this.openVidu.httpClient.execute(request); JsonObject json = connectionProperties.toJson(sessionId);
int statusCode = response.getStatusLine().getStatusCode(); HttpRequest request = this.openVidu.addHeaders(HttpRequest.newBuilder()
if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { .POST(HttpRequest.BodyPublishers.ofString(json.toString()))
Connection connection = new Connection(httpResponseToJson(response)); .uri(new URI(this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/connection"))
.setHeader("Content-Type", "application/json")
.timeout(Duration.ofMillis(this.openVidu.requestTimeout))).build();
HttpResponse<String> response = this.openVidu.httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == HttpURLConnection.HTTP_OK) {
Connection connection = new Connection(Utils.httpResponseToJson(response));
this.connections.put(connection.getConnectionId(), connection); this.connections.put(connection.getConnectionId(), connection);
return connection; return connection;
} else { } else {
throw new OpenViduHttpException(statusCode); throw new OpenViduHttpException(response.statusCode());
} }
} catch (IOException e) { } catch (URISyntaxException | IOException | InterruptedException e) {
throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
} finally {
if (response != null) {
EntityUtils.consumeQuietly(response.getEntity());
}
} }
} }
@ -220,27 +213,24 @@ public class Session {
* @throws OpenViduHttpException * @throws OpenViduHttpException
*/ */
public void close() throws OpenViduJavaClientException, OpenViduHttpException { public void close() throws OpenViduJavaClientException, OpenViduHttpException {
HttpDelete request = new HttpDelete(this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId);
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded");
HttpResponse response = null;
try { try {
response = this.openVidu.httpClient.execute(request); HttpRequest request = this.openVidu.addHeaders(HttpRequest.newBuilder().DELETE()
.uri(new URI(this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId))
.timeout(Duration.ofMillis(this.openVidu.requestTimeout))).build();
int statusCode = response.getStatusLine().getStatusCode(); HttpResponse<String> response = this.openVidu.httpClient.send(request,
if ((statusCode == org.apache.http.HttpStatus.SC_NO_CONTENT)) { HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == HttpURLConnection.HTTP_NO_CONTENT) {
this.openVidu.activeSessions.remove(this.sessionId); this.openVidu.activeSessions.remove(this.sessionId);
log.info("Session {} closed", this.sessionId); log.info("Session {} closed", this.sessionId);
} else { } else {
throw new OpenViduHttpException(statusCode); throw new OpenViduHttpException(response.statusCode());
} }
} catch (IOException e) { } catch (URISyntaxException | IOException | InterruptedException e) {
throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
} finally {
if (response != null) {
EntityUtils.consumeQuietly(response.getEntity());
}
} }
} }
@ -266,32 +256,30 @@ public class Session {
* @throws OpenViduJavaClientException * @throws OpenViduJavaClientException
*/ */
public boolean fetch() throws OpenViduJavaClientException, OpenViduHttpException { public boolean fetch() throws OpenViduJavaClientException, OpenViduHttpException {
final String beforeJSON = this.toJson();
HttpGet request = new HttpGet(
this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId + "?pendingConnections=true");
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded");
HttpResponse response = null;
try { try {
response = this.openVidu.httpClient.execute(request); final String beforeJSON = this.toJson();
int statusCode = response.getStatusLine().getStatusCode(); HttpRequest request = this.openVidu.addHeaders(HttpRequest.newBuilder().GET()
if ((statusCode == org.apache.http.HttpStatus.SC_OK)) { .uri(new URI(this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId
this.resetWithJson(httpResponseToJson(response)); + "?pendingConnections=true"))
.timeout(Duration.ofMillis(this.openVidu.requestTimeout))).build();
HttpResponse<String> response = this.openVidu.httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == HttpURLConnection.HTTP_OK) {
this.resetWithJson(Utils.httpResponseToJson(response));
final String afterJSON = this.toJson(); final String afterJSON = this.toJson();
boolean hasChanged = !beforeJSON.equals(afterJSON); boolean hasChanged = !beforeJSON.equals(afterJSON);
log.info("Session info fetched for session '{}'. Any change: {}", this.sessionId, hasChanged); log.info("Session info fetched for session '{}'. Any change: {}", this.sessionId, hasChanged);
return hasChanged; return hasChanged;
} else { } else {
throw new OpenViduHttpException(statusCode); throw new OpenViduHttpException(response.statusCode());
} }
} catch (IOException e) { } catch (URISyntaxException | IOException | InterruptedException e) {
throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
} finally {
if (response != null) {
EntityUtils.consumeQuietly(response.getEntity());
}
} }
} }
@ -340,16 +328,18 @@ public class Session {
* @throws OpenViduHttpException * @throws OpenViduHttpException
*/ */
public void forceDisconnect(String connectionId) throws OpenViduJavaClientException, OpenViduHttpException { public void forceDisconnect(String connectionId) throws OpenViduJavaClientException, OpenViduHttpException {
HttpDelete request = new HttpDelete(
this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/connection/" + connectionId);
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded");
HttpResponse response = null;
try { try {
response = this.openVidu.httpClient.execute(request);
int statusCode = response.getStatusLine().getStatusCode(); HttpRequest request = this.openVidu.addHeaders(HttpRequest
if ((statusCode == org.apache.http.HttpStatus.SC_NO_CONTENT)) { .newBuilder().DELETE().uri(new URI(this.openVidu.hostname + OpenVidu.API_SESSIONS + "/"
+ this.sessionId + "/connection/" + connectionId))
.timeout(Duration.ofMillis(this.openVidu.requestTimeout))).build();
HttpResponse<String> response = this.openVidu.httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == HttpURLConnection.HTTP_NO_CONTENT) {
// Remove connection from activeConnections map // Remove connection from activeConnections map
Connection connectionClosed = this.connections.remove(connectionId); Connection connectionClosed = this.connections.remove(connectionId);
// Remove every Publisher of the closed connection from every subscriber list of // Remove every Publisher of the closed connection from every subscriber list of
@ -368,15 +358,11 @@ public class Session {
} }
log.info("Connection {} closed", connectionId); log.info("Connection {} closed", connectionId);
} else { } else {
throw new OpenViduHttpException(statusCode); throw new OpenViduHttpException(response.statusCode());
} }
} catch (IOException e) { } catch (URISyntaxException | IOException | InterruptedException e) {
throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
} finally {
if (response != null) {
EntityUtils.consumeQuietly(response.getEntity());
}
} }
} }
@ -422,16 +408,20 @@ public class Session {
* @throws OpenViduHttpException * @throws OpenViduHttpException
*/ */
public void forceUnpublish(String streamId) throws OpenViduJavaClientException, OpenViduHttpException { public void forceUnpublish(String streamId) throws OpenViduJavaClientException, OpenViduHttpException {
HttpDelete request = new HttpDelete(
this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/stream/" + streamId);
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded");
HttpResponse response = null;
try { try {
response = this.openVidu.httpClient.execute(request);
int statusCode = response.getStatusLine().getStatusCode(); HttpRequest request = this.openVidu
if ((statusCode == org.apache.http.HttpStatus.SC_NO_CONTENT)) { .addHeaders(HttpRequest.newBuilder().DELETE()
.uri(new URI(this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId
+ "/stream/" + streamId))
.timeout(Duration.ofMillis(this.openVidu.requestTimeout)))
.build();
HttpResponse<String> response = this.openVidu.httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == HttpURLConnection.HTTP_NO_CONTENT) {
for (Connection connection : this.connections.values()) { for (Connection connection : this.connections.values()) {
// Try to remove the Publisher from the Connection publishers collection // Try to remove the Publisher from the Connection publishers collection
if (connection.publishers.remove(streamId) != null) { if (connection.publishers.remove(streamId) != null) {
@ -442,15 +432,11 @@ public class Session {
} }
log.info("Stream {} unpublished", streamId); log.info("Stream {} unpublished", streamId);
} else { } else {
throw new OpenViduHttpException(statusCode); throw new OpenViduHttpException(response.statusCode());
} }
} catch (IOException e) { } catch (URISyntaxException | IOException | InterruptedException e) {
throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
} finally {
if (response != null) {
EntityUtils.consumeQuietly(response.getEntity());
}
} }
} }
@ -495,26 +481,28 @@ public class Session {
public Connection updateConnection(String connectionId, ConnectionProperties connectionProperties) public Connection updateConnection(String connectionId, ConnectionProperties connectionProperties)
throws OpenViduJavaClientException, OpenViduHttpException { throws OpenViduJavaClientException, OpenViduHttpException {
HttpPatch request = new HttpPatch(
this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/connection/" + connectionId);
StringEntity params = new StringEntity(connectionProperties.toJson(this.sessionId).toString(), "UTF-8");
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
request.setEntity(params);
HttpResponse response = null;
try { try {
response = this.openVidu.httpClient.execute(request);
int statusCode = response.getStatusLine().getStatusCode(); JsonObject jsonPayload = connectionProperties.toJson(this.sessionId);
if ((statusCode == org.apache.http.HttpStatus.SC_OK)) {
HttpRequest request = this.openVidu.addHeaders(HttpRequest.newBuilder()
.method("PATCH", HttpRequest.BodyPublishers.ofString(jsonPayload.toString()))
.uri(new URI(this.openVidu.hostname + OpenVidu.API_SESSIONS + "/" + this.sessionId + "/connection/"
+ connectionId))
.setHeader("Content-Type", "application/json")
.timeout(Duration.ofMillis(this.openVidu.requestTimeout))).build();
HttpResponse<String> response = this.openVidu.httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == HttpURLConnection.HTTP_OK) {
log.info("Connection {} updated", connectionId); log.info("Connection {} updated", connectionId);
} else if ((statusCode == org.apache.http.HttpStatus.SC_NO_CONTENT)) { } else if (response.statusCode() == HttpURLConnection.HTTP_NO_CONTENT) {
log.info("Properties of Connection {} remain the same", connectionId); log.info("Properties of Connection {} remain the same", connectionId);
} else { } else {
throw new OpenViduHttpException(statusCode); throw new OpenViduHttpException(response.statusCode());
} }
JsonObject json = httpResponseToJson(response); JsonObject json = Utils.httpResponseToJson(response);
// Update the actual Connection object with the new options // Update the actual Connection object with the new options
Connection existingConnection = this.connections.get(connectionId); Connection existingConnection = this.connections.get(connectionId);
@ -530,12 +518,8 @@ public class Session {
return existingConnection; return existingConnection;
} }
} catch (IOException e) { } catch (URISyntaxException | IOException | InterruptedException e) {
throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
} finally {
if (response != null) {
EntityUtils.consumeQuietly(response.getEntity());
}
} }
} }
@ -642,23 +626,27 @@ public class Session {
} }
private void getSessionHttp() throws OpenViduJavaClientException, OpenViduHttpException { private void getSessionHttp() throws OpenViduJavaClientException, OpenViduHttpException {
if (this.hasSessionId()) { if (this.hasSessionId()) {
return; return;
} }
HttpPost request = new HttpPost(this.openVidu.hostname + OpenVidu.API_SESSIONS);
StringEntity params = new StringEntity(properties.toJson().toString(), "UTF-8");
request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
request.setEntity(params);
HttpResponse response = null;
try { try {
response = this.openVidu.httpClient.execute(request);
int statusCode = response.getStatusLine().getStatusCode(); JsonObject json = properties.toJson();
if ((statusCode == org.apache.http.HttpStatus.SC_OK)) {
JsonObject responseJson = httpResponseToJson(response); HttpRequest request = this.openVidu
.addHeaders(HttpRequest.newBuilder().POST(HttpRequest.BodyPublishers.ofString(json.toString()))
.uri(new URI(this.openVidu.hostname + OpenVidu.API_SESSIONS))
.setHeader("Content-Type", "application/json")
.timeout(Duration.ofMillis(this.openVidu.requestTimeout)))
.build();
HttpResponse<String> response = this.openVidu.httpClient.send(request,
HttpResponse.BodyHandlers.ofString());
if (response.statusCode() == HttpURLConnection.HTTP_OK) {
JsonObject responseJson = Utils.httpResponseToJson(response);
this.sessionId = responseJson.get("id").getAsString(); this.sessionId = responseJson.get("id").getAsString();
this.createdAt = responseJson.get("createdAt").getAsLong(); this.createdAt = responseJson.get("createdAt").getAsLong();
@ -675,31 +663,17 @@ public class Session {
this.properties = responseProperties; this.properties = responseProperties;
log.info("Session '{}' created", this.sessionId); log.info("Session '{}' created", this.sessionId);
} else if (statusCode == org.apache.http.HttpStatus.SC_CONFLICT) { } else if (response.statusCode() == HttpURLConnection.HTTP_CONFLICT) {
// 'customSessionId' already existed // 'customSessionId' already existed
this.sessionId = properties.customSessionId(); this.sessionId = properties.customSessionId();
this.fetch(); this.fetch();
} else { } else {
throw new OpenViduHttpException(statusCode); throw new OpenViduHttpException(response.statusCode());
} }
} catch (IOException e) { } catch (URISyntaxException | IOException | InterruptedException e) {
throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
} finally {
if (response != null) {
EntityUtils.consumeQuietly(response.getEntity());
}
}
}
private JsonObject httpResponseToJson(HttpResponse response) throws OpenViduJavaClientException {
JsonObject json;
try {
json = new Gson().fromJson(EntityUtils.toString(response.getEntity(), "UTF-8"), JsonObject.class);
} catch (JsonSyntaxException | IOException e) {
throw new OpenViduJavaClientException(e.getMessage(), e.getCause()); throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
} }
return json;
} }
protected void setIsBeingRecorded(boolean recording) { protected void setIsBeingRecorded(boolean recording) {

View File

@ -1,9 +1,24 @@
package io.openvidu.java.client.utils; package io.openvidu.java.client;
import java.net.http.HttpResponse;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
/** /**
* @hidden * @hidden
*/ */
public final class FormatChecker { public class Utils {
public static JsonObject httpResponseToJson(HttpResponse<String> response) throws OpenViduJavaClientException {
try {
JsonObject json = new Gson().fromJson(response.body(), JsonObject.class);
return json;
} catch (JsonSyntaxException e) {
throw new OpenViduJavaClientException(e.getMessage(), e.getCause());
}
}
public static boolean isAcceptableRecordingResolution(String stringResolution) { public static boolean isAcceptableRecordingResolution(String stringResolution) {
// Matches every string with format "AxB", being A and B any number not starting // Matches every string with format "AxB", being A and B any number not starting

View File

@ -0,0 +1,70 @@
package io.openvidu.java.client.test;
import java.net.Authenticator;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.ProxySelector;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Redirect;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.Executors;
import javax.net.ssl.SSLContext;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import io.openvidu.java.client.OpenVidu;
public class OpenViduConstructorTest {
@Test
public void wrongHostname() {
RuntimeException thrown = Assertions.assertThrows(RuntimeException.class, () -> {
new OpenVidu("WRONG_URL", "MY_SECRET");
});
Assertions.assertEquals("The hostname \"WRONG_URL\" is not a valid URL: no protocol: WRONG_URL",
thrown.getMessage());
}
@Test
public void buildWithHttpClientWithoutAuthenticator() {
HttpClient.Builder builder = HttpClient.newBuilder();
builder.connectTimeout(Duration.ofMillis(10000));
ProxySelector proxy = ProxySelector.of(new InetSocketAddress("https://my.proxy.hostname/", 4444));
builder.proxy(proxy);
builder.followRedirects(Redirect.ALWAYS);
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
} catch (Exception e) {
}
builder.sslContext(sslContext);
builder.executor(Executors.newFixedThreadPool(1));
builder.cookieHandler(new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER));
OpenVidu OV = new OpenVidu("https://localhost:4443/", "MY_SECRET", builder.build());
Assertions.assertEquals(30000, OV.getRequestTimeout());
Assertions.assertTrue(OV.getRequestHeaders().isEmpty());
OV.setRequestTimeout(5000);
OV.setRequestHeaders(Map.of("header1", "value1", "header2", "value2"));
Assertions.assertEquals(5000, OV.getRequestTimeout());
Assertions.assertEquals(2, OV.getRequestHeaders().size());
}
@Test
public void buildWithHttpClientWithAuthenticator() {
Authenticator authenticator = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("OPENVIDUAPP", "secret".toCharArray());
}
};
HttpClient.Builder builder = HttpClient.newBuilder().authenticator(authenticator);
new OpenVidu("https://localhost:4443/", "MY_SECRET", builder.build());
}
}

View File

@ -6,9 +6,9 @@ import java.util.List;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import io.openvidu.java.client.utils.FormatChecker; import io.openvidu.java.client.Utils;
public class FormatCheckerTest { public class UtilsFormatCheckerTest {
@Test @Test
public void testCustomSessionIdFormat() { public void testCustomSessionIdFormat() {
@ -23,9 +23,9 @@ public class FormatCheckerTest {
"session-_", "123_session-1"); "session-_", "123_session-1");
for (String id : invalidCustomSessionIds) for (String id : invalidCustomSessionIds)
Assertions.assertFalse(FormatChecker.isValidCustomSessionId(id)); Assertions.assertFalse(Utils.isValidCustomSessionId(id));
for (String id : validCustomSessionIds) for (String id : validCustomSessionIds)
Assertions.assertTrue(FormatChecker.isValidCustomSessionId(id)); Assertions.assertTrue(Utils.isValidCustomSessionId(id));
} }
@Test @Test
@ -37,9 +37,9 @@ public class FormatCheckerTest {
List<String> validResolutions = Arrays.asList("1920x1080", "1280x720", "100x1999"); List<String> validResolutions = Arrays.asList("1920x1080", "1280x720", "100x1999");
for (String resolution : invalidResolutions) for (String resolution : invalidResolutions)
Assertions.assertFalse(FormatChecker.isAcceptableRecordingResolution(resolution)); Assertions.assertFalse(Utils.isAcceptableRecordingResolution(resolution));
for (String resolution : validResolutions) for (String resolution : validResolutions)
Assertions.assertTrue(FormatChecker.isAcceptableRecordingResolution(resolution)); Assertions.assertTrue(Utils.isAcceptableRecordingResolution(resolution));
} }
@Test @Test
@ -50,9 +50,9 @@ public class FormatCheckerTest {
List<Integer> validFramerates = Arrays.asList(1, 2, 30, 60, 119, 120); List<Integer> validFramerates = Arrays.asList(1, 2, 30, 60, 119, 120);
for (int framerate : invalidFrameRates) for (int framerate : invalidFrameRates)
Assertions.assertFalse(FormatChecker.isAcceptableRecordingFrameRate(framerate)); Assertions.assertFalse(Utils.isAcceptableRecordingFrameRate(framerate));
for (int framerate : validFramerates) for (int framerate : validFramerates)
Assertions.assertTrue(FormatChecker.isAcceptableRecordingFrameRate(framerate)); Assertions.assertTrue(Utils.isAcceptableRecordingFrameRate(framerate));
} }
} }

View File

@ -87,10 +87,10 @@ export class OpenVidu {
activeSessions: Session[] = []; activeSessions: Session[] = [];
/** /**
* @param hostname URL where your instance of OpenVidu Server is up an running. * @param hostname URL where your OpenVidu deployment is up an running.
* It must be the full URL (e.g. `https://12.34.56.78:1234/`) * It must be the full URL (e.g. `https://12.34.56.78:1234/`)
* *
* @param secret Secret used on OpenVidu Server initialization * @param secret Secret configured in your OpenVidu deployment
*/ */
constructor(private hostname: string, secret: string) { constructor(private hostname: string, secret: string) {
this.setHostnameAndPort(); this.setHostnameAndPort();

View File

@ -53,7 +53,6 @@ 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.utils.FormatChecker;
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;
@ -338,7 +337,7 @@ 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, List<IceServerProperties> customIceServers) throws Exception { KurentoOptions kurentoOptions, List<IceServerProperties> customIceServers) throws Exception {
if (!FormatChecker.isServerMetadataFormatCorrect(serverMetadata)) { if (!io.openvidu.java.client.Utils.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");
} }

View File

@ -49,7 +49,6 @@ import io.openvidu.client.internal.ProtocolElements;
import io.openvidu.java.client.ConnectionProperties; import io.openvidu.java.client.ConnectionProperties;
import io.openvidu.java.client.ConnectionType; import io.openvidu.java.client.ConnectionType;
import io.openvidu.java.client.OpenViduRole; import io.openvidu.java.client.OpenViduRole;
import io.openvidu.java.client.utils.FormatChecker;
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.core.EndReason; import io.openvidu.server.core.EndReason;
@ -304,7 +303,7 @@ public class RpcHandler extends DefaultJsonRpcHandler<JsonObject> {
if (tokenObj != null) { if (tokenObj != null) {
String clientMetadata = getStringParam(request, ProtocolElements.JOINROOM_METADATA_PARAM); String clientMetadata = getStringParam(request, ProtocolElements.JOINROOM_METADATA_PARAM);
if (FormatChecker.isServerMetadataFormatCorrect(clientMetadata)) { if (io.openvidu.java.client.Utils.isServerMetadataFormatCorrect(clientMetadata)) {
// While closing a session users can't join // While closing a session users can't join
if (session.closingLock.readLock().tryLock()) { if (session.closingLock.readLock().tryLock()) {

View File

@ -102,6 +102,11 @@
<artifactId>java-client</artifactId> <artifactId>java-client</artifactId>
<version>${version.appium}</version> <version>${version.appium}</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
</dependencies> </dependencies>
<profiles> <profiles>

View File

@ -112,6 +112,11 @@
<artifactId>java-string-similarity</artifactId> <artifactId>java-string-similarity</artifactId>
<version>${version.stringsimilarity}</version> <version>${version.stringsimilarity}</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -20,6 +20,13 @@ package io.openvidu.test.e2e;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
import java.io.File; import java.io.File;
import java.net.Authenticator;
import java.net.PasswordAuthentication;
import java.net.Socket;
import java.net.http.HttpClient;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Arrays; import java.util.Arrays;
import java.util.Base64; import java.util.Base64;
import java.util.Collection; import java.util.Collection;
@ -34,6 +41,12 @@ import java.util.concurrent.TimeoutException;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedTrustManager;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.http.HttpStatus; import org.apache.http.HttpStatus;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
@ -2427,6 +2440,129 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestappE2eTest {
gracefullyLeaveParticipants(user, 2); gracefullyLeaveParticipants(user, 2);
} }
@Test
@DisplayName("openvidu-java-client custom HttpClient test")
void openViduJavaClientCustomHttpClientTest() throws Exception {
// Test all possible combinations: custom Authenticator present and valid,
// present and wrong and no present; in combination with custom Authorization
// header present and valid, present and wrong and no present
HttpClient.Builder builder = HttpClient.newBuilder();
SSLContext sslContext;
try {
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { new X509ExtendedTrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(final X509Certificate[] a_certificates, final String a_auth_type) {
}
public void checkServerTrusted(final X509Certificate[] a_certificates, final String a_auth_type) {
}
public void checkClientTrusted(final X509Certificate[] a_certificates, final String a_auth_type,
final Socket a_socket) {
}
public void checkServerTrusted(final X509Certificate[] a_certificates, final String a_auth_type,
final Socket a_socket) {
}
public void checkClientTrusted(final X509Certificate[] a_certificates, final String a_auth_type,
final SSLEngine a_engine) {
}
public void checkServerTrusted(final X509Certificate[] a_certificates, final String a_auth_type,
final SSLEngine a_engine) {
}
} }, null);
} catch (KeyManagementException | NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
builder.sslContext(sslContext);
final String BASIC_AUTH = "Basic "
+ Base64.getEncoder().encodeToString(("OPENVIDUAPP:" + OPENVIDU_SECRET).getBytes());
final String WRONG_SECRET = "WRONG_SECRET_" + RandomStringUtils.randomAlphanumeric(10);
// 1. No authenticator, no header, 200
OpenVidu customHttpClientOV1 = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET, builder.build());
customHttpClientOV1.fetch();
// 2. No authenticator, wrong header, 401
customHttpClientOV1.setRequestHeaders(Map.of("Authorization", "WRONG_AUTH_HEADER"));
OpenViduHttpException thrown = Assertions.assertThrows(OpenViduHttpException.class, () -> {
customHttpClientOV1.fetch();
});
Assertions.assertEquals(401, thrown.getStatus());
// 3. No authenticator and valid header, 200
customHttpClientOV1.setRequestHeaders(Map.of("Authorization", BASIC_AUTH));
customHttpClientOV1.fetch();
// 4. Wrong authenticator and no header, 401
builder.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("OPENVIDUAPP", WRONG_SECRET.toCharArray());
}
});
OpenVidu customHttpClientOV2 = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET, builder.build());
OpenViduJavaClientException thrown2 = Assertions.assertThrows(OpenViduJavaClientException.class, () -> {
customHttpClientOV2.fetch();
});
Assertions.assertTrue(thrown2.getMessage().contains("too many authentication attempts"));
// 5. Wrong authenticator and wrong header, 401
customHttpClientOV2.setRequestHeaders(Map.of("Authorization", "WRONG_AUTH_HEADER"));
thrown2 = Assertions.assertThrows(OpenViduJavaClientException.class, () -> {
customHttpClientOV2.fetch();
});
Assertions.assertTrue(thrown2.getMessage().contains("too many authentication attempts"));
// 6. Wrong authenticator and valid header, 401
customHttpClientOV2.setRequestHeaders(Map.of("Authorization", BASIC_AUTH));
thrown2 = Assertions.assertThrows(OpenViduJavaClientException.class, () -> {
customHttpClientOV2.fetch();
});
Assertions.assertTrue(thrown2.getMessage().contains("too many authentication attempts"));
// 7. Valid authenticator and no header, 200
builder.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication("OPENVIDUAPP", OPENVIDU_SECRET.toCharArray());
}
});
OpenVidu customHttpClientOV3 = new OpenVidu(OPENVIDU_URL, OPENVIDU_SECRET, builder.build());
customHttpClientOV3.fetch();
// 8. Valid authenticator and wrong header, 200
customHttpClientOV3.setRequestHeaders(Map.of("Authorization", "WRONG_AUTH_HEADER"));
customHttpClientOV3.fetch();
// 9. Valid authenticator and valid header, 200
customHttpClientOV3.setRequestHeaders(Map.of("Authorization", BASIC_AUTH));
customHttpClientOV3.fetch();
// 10. Wrong secret, valid authenticator, no header, 200
OpenVidu customHttpClientOV4 = new OpenVidu(OPENVIDU_URL, WRONG_SECRET, builder.build());
customHttpClientOV4.fetch();
// 11. Wrong secret, valid authenticator, wrong header, 200
customHttpClientOV4.setRequestHeaders(Map.of("Authorization", "WRONG_AUTH_HEADER"));
customHttpClientOV4.fetch();
// 12. Wrong secret, no authenticator, valid header, 200
builder = HttpClient.newBuilder().sslContext(sslContext);
customHttpClientOV4 = new OpenVidu(OPENVIDU_URL, WRONG_SECRET, builder.build());
customHttpClientOV4.setRequestHeaders(Map.of("Authorization", BASIC_AUTH));
customHttpClientOV4.fetch();
}
@Test @Test
@DisplayName("openvidu-java-client test") @DisplayName("openvidu-java-client test")
void openViduJavaClientTest() throws Exception { void openViduJavaClientTest() throws Exception {