From 759acb1f7c4ea092992d466b76b29f2ccf736fe9 Mon Sep 17 00:00:00 2001 From: pabloFuente Date: Mon, 19 Oct 2020 12:52:29 +0200 Subject: [PATCH] openvidu-server: POST Connection --- .../openvidu/server/core/ConnectionType.java | 5 + .../io/openvidu/server/core/Participant.java | 13 +- .../openvidu/server/core/SessionManager.java | 14 +- .../java/io/openvidu/server/core/Token.java | 1 + .../kurento/core/KurentoParticipant.java | 2 +- .../server/rest/SessionRestController.java | 447 ++++++++++-------- ...essionGarbageCollectorIntegrationTest.java | 2 +- .../test/e2e/OpenViduProTestAppE2eTest.java | 21 +- .../test/e2e/OpenViduTestAppE2eTest.java | 13 +- 9 files changed, 302 insertions(+), 216 deletions(-) create mode 100644 openvidu-server/src/main/java/io/openvidu/server/core/ConnectionType.java diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/ConnectionType.java b/openvidu-server/src/main/java/io/openvidu/server/core/ConnectionType.java new file mode 100644 index 00000000..a5206826 --- /dev/null +++ b/openvidu-server/src/main/java/io/openvidu/server/core/ConnectionType.java @@ -0,0 +1,5 @@ +package io.openvidu.server.core; + +public enum ConnectionType { + WEBRTC, IPCAM +} \ No newline at end of file diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java b/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java index 9a638013..b20f0220 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/Participant.java @@ -43,6 +43,7 @@ public class Participant { active } + protected ConnectionType type; // WEBRTC, IPCAM protected String finalUserId; // ID to match this connection with a final user (HttpSession id) protected String participantPrivatetId; // ID to identify the user on server (org.kurento.jsonrpc.Session.id) protected String participantPublicId; // ID to identify the user on clients @@ -76,9 +77,10 @@ public class Participant { */ public Lock singleRecordingLock = new ReentrantLock(); - public Participant(String finalUserId, String participantPrivatetId, String participantPublicId, String sessionId, - Token token, String clientMetadata, GeoLocation location, String platform, EndpointType endpointType, - Long activeAt) { + public Participant(ConnectionType type, String finalUserId, String participantPrivatetId, + String participantPublicId, String sessionId, Token token, String clientMetadata, GeoLocation location, + String platform, EndpointType endpointType, Long activeAt) { + this.type = type; this.finalUserId = finalUserId; this.participantPrivatetId = participantPrivatetId; this.participantPublicId = participantPublicId; @@ -101,6 +103,10 @@ public class Participant { this.endpointType = endpointType; } + public ConnectionType getType() { + return type; + } + public String getFinalUserId() { return finalUserId; } @@ -300,6 +306,7 @@ public class Participant { JsonObject json = new JsonObject(); json.addProperty("id", this.participantPublicId); json.addProperty("object", "connection"); + json.addProperty("type", this.type.name()); json.addProperty("status", this.status.name()); json.addProperty("connectionId", this.participantPublicId); // TODO: deprecated. Better use only "id" json.addProperty("sessionId", this.sessionId); diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java b/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java index 75e75e44..e6f4fcaf 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/SessionManager.java @@ -363,8 +363,9 @@ public abstract class SessionManager { if (this.sessionidParticipantpublicidParticipant.get(sessionId) != null) { - Participant p = new Participant(finalUserId, participantPrivatetId, token.getConnectionId(), sessionId, - token, clientMetadata, location, platform, EndpointType.WEBRTC_ENDPOINT, null); + Participant p = new Participant(ConnectionType.WEBRTC, finalUserId, participantPrivatetId, + token.getConnectionId(), sessionId, token, clientMetadata, location, platform, + EndpointType.WEBRTC_ENDPOINT, null); this.sessionidParticipantpublicidParticipant.get(sessionId).put(p.getParticipantPublicId(), p); @@ -384,8 +385,9 @@ public abstract class SessionManager { public Participant newRecorderParticipant(String sessionId, String participantPrivatetId, Token token, String clientMetadata) { if (this.sessionidParticipantpublicidParticipant.get(sessionId) != null) { - Participant p = new Participant(null, participantPrivatetId, ProtocolElements.RECORDER_PARTICIPANT_PUBLICID, - sessionId, token, clientMetadata, null, null, EndpointType.WEBRTC_ENDPOINT, null); + Participant p = new Participant(ConnectionType.WEBRTC, null, participantPrivatetId, + ProtocolElements.RECORDER_PARTICIPANT_PUBLICID, sessionId, token, clientMetadata, null, null, + EndpointType.WEBRTC_ENDPOINT, null); this.sessionidParticipantpublicidParticipant.get(sessionId) .put(ProtocolElements.RECORDER_PARTICIPANT_PUBLICID, p); return p; @@ -397,8 +399,8 @@ public abstract class SessionManager { public Participant newIpcamParticipant(String sessionId, String ipcamId, Token token, GeoLocation location, String platform) { if (this.sessionidParticipantpublicidParticipant.get(sessionId) != null) { - Participant p = new Participant(ipcamId, ipcamId, ipcamId, sessionId, token, null, location, platform, - EndpointType.PLAYER_ENDPOINT, null); + Participant p = new Participant(ConnectionType.IPCAM, ipcamId, ipcamId, ipcamId, sessionId, token, null, + location, platform, EndpointType.PLAYER_ENDPOINT, null); this.sessionidParticipantpublicidParticipant.get(sessionId).put(ipcamId, p); return p; } else { diff --git a/openvidu-server/src/main/java/io/openvidu/server/core/Token.java b/openvidu-server/src/main/java/io/openvidu/server/core/Token.java index 947c99f3..85411ce1 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/core/Token.java +++ b/openvidu-server/src/main/java/io/openvidu/server/core/Token.java @@ -117,6 +117,7 @@ public class Token { JsonObject json = new JsonObject(); json.addProperty("id", this.getConnectionId()); json.addProperty("object", "connection"); + json.addProperty("type", ConnectionType.WEBRTC.name()); json.addProperty("status", ParticipantStatus.pending.name()); json.addProperty("connectionId", this.getConnectionId()); // DEPRECATED: better use id json.addProperty("sessionId", this.sessionId); diff --git a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java index 306e5034..6af8375d 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java +++ b/openvidu-server/src/main/java/io/openvidu/server/kurento/core/KurentoParticipant.java @@ -77,7 +77,7 @@ public class KurentoParticipant extends Participant { public KurentoParticipant(Participant participant, KurentoSession kurentoSession, KurentoParticipantEndpointConfig endpointConfig, OpenviduConfig openviduConfig, RecordingManager recordingManager) { - super(participant.getFinalUserId(), participant.getParticipantPrivateId(), participant.getParticipantPublicId(), + super(participant.getType(), participant.getFinalUserId(), participant.getParticipantPrivateId(), participant.getParticipantPublicId(), kurentoSession.getSessionId(), participant.getToken(), participant.getClientMetadata(), participant.getLocation(), participant.getPlatform(), participant.getEndpointType(), participant.getActiveAt()); diff --git a/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java b/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java index 7992af8a..ca3ef2dc 100644 --- a/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java +++ b/openvidu-server/src/main/java/io/openvidu/server/rest/SessionRestController.java @@ -20,6 +20,7 @@ package io.openvidu.server.rest; import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; @@ -57,6 +58,7 @@ import io.openvidu.java.client.RecordingMode; import io.openvidu.java.client.RecordingProperties; import io.openvidu.java.client.SessionProperties; import io.openvidu.server.config.OpenviduConfig; +import io.openvidu.server.core.ConnectionType; import io.openvidu.server.core.EndReason; import io.openvidu.server.core.IdentifierPrefixes; import io.openvidu.server.core.Participant; @@ -92,7 +94,7 @@ public class SessionRestController { protected OpenviduConfig openviduConfig; @RequestMapping(value = "/sessions", method = RequestMethod.POST) - public ResponseEntity getSessionId(@RequestBody(required = false) Map params) { + public ResponseEntity initializeSession(@RequestBody(required = false) Map params) { log.info("REST API: POST {}/sessions {}", RequestMappings.API, params != null ? params.toString() : "{}"); @@ -267,8 +269,99 @@ public class SessionRestController { } } + @RequestMapping(value = "/sessions/{sessionId}/connection", method = RequestMethod.POST) + public ResponseEntity initializeConnection(@PathVariable("sessionId") String sessionId, + @RequestBody Map params) { + + log.info("REST API: POST {}/sessions/{}/connection {}", RequestMappings.API, sessionId, params.toString()); + + Session session = this.sessionManager.getSessionWithNotActive(sessionId); + if (session == null) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + + String typeString; + String data; + Boolean record; + try { + typeString = (String) params.get("type"); + data = (String) params.get("data"); + record = (Boolean) params.get("record"); + } catch (ClassCastException e) { + return this.generateErrorResponse("Type error in parameter \"type\"", + "/sessions/" + sessionId + "/connection", HttpStatus.BAD_REQUEST); + } + + ConnectionType type; + try { + if (typeString != null) { + type = ConnectionType.valueOf(typeString); + } else { + type = ConnectionType.WEBRTC; + } + } catch (IllegalArgumentException e) { + return this.generateErrorResponse("Parameter type " + params.get("typeString") + " is not defined", + RequestMappings.API + "/sessions/" + sessionId + "/connection", HttpStatus.BAD_REQUEST); + } + + switch (type) { + case WEBRTC: + return this.newWebrtcConnection(session, data, record, params); + case IPCAM: + return this.newIpcamConnection(session, data, record, params); + default: + return this.generateErrorResponse("Wrong type " + typeString, + RequestMappings.API + "/sessions/" + sessionId + "/connection", HttpStatus.BAD_REQUEST); + } + } + + @RequestMapping(value = "/sessions/{sessionId}/connection/{connectionId}", method = RequestMethod.GET) + public ResponseEntity getConnection(@PathVariable("sessionId") String sessionId, + @PathVariable("connectionId") String connectionId) { + + log.info("REST API: GET {}/sessions/{}/connection/{}", RequestMappings.API, sessionId, connectionId); + + Session session = this.sessionManager.getSessionWithNotActive(sessionId); + if (session != null) { + Participant p = session.getParticipantByPublicId(connectionId); + if (p != null) { + return new ResponseEntity<>(p.toJson().toString(), RestUtils.getResponseHeaders(), HttpStatus.OK); + } else { + Token t = getTokenFromConnectionId(connectionId, session.getTokenIterator()); + if (t != null) { + return new ResponseEntity<>(t.toJsonAsParticipant().toString(), RestUtils.getResponseHeaders(), + HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + } else { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + } + + @RequestMapping(value = "/sessions/{sessionId}/connection", method = RequestMethod.GET) + public ResponseEntity listConnections(@PathVariable("sessionId") String sessionId, + @RequestParam(value = "pendingConnections", defaultValue = "true", required = false) boolean pendingConnections, + @RequestParam(value = "webRtcStats", defaultValue = "false", required = false) boolean webRtcStats) { + + log.info("REST API: GET {}/sessions/{}/connection", RequestMappings.API, sessionId); + + Session session = this.sessionManager.getSessionWithNotActive(sessionId); + + if (session != null) { + JsonObject json = new JsonObject(); + JsonArray jsonArray = session.getSnapshotOfConnectionsAsJsonArray(pendingConnections, webRtcStats); + json.addProperty("numberOfElements", jsonArray.size()); + json.add("content", jsonArray); + return new ResponseEntity<>(json.toString(), RestUtils.getResponseHeaders(), HttpStatus.OK); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + @RequestMapping(value = "/sessions/{sessionId}/connection/{connectionId}", method = RequestMethod.DELETE) - public ResponseEntity disconnectParticipant(@PathVariable("sessionId") String sessionId, + public ResponseEntity closeConnection(@PathVariable("sessionId") String sessionId, @PathVariable("connectionId") String participantPublicId) { log.info("REST API: DELETE {}/sessions/{}/connection/{}", RequestMappings.API, sessionId, participantPublicId); @@ -292,127 +385,8 @@ public class SessionRestController { } } - @RequestMapping(value = "/sessions/{sessionId}/stream/{streamId}", method = RequestMethod.DELETE) - public ResponseEntity unpublishStream(@PathVariable("sessionId") String sessionId, - @PathVariable("streamId") String streamId) { - - log.info("REST API: DELETE {}/sessions/{}/stream/{}", RequestMappings.API, sessionId, streamId); - - Session session = this.sessionManager.getSessionWithNotActive(sessionId); - if (session == null) { - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); - } - - session = this.sessionManager.getSession(sessionId); - if (session != null) { - - final String participantPrivateId = this.sessionManager.getParticipantPrivateIdFromStreamId(sessionId, - streamId); - - if (participantPrivateId == null) { - return new ResponseEntity<>(HttpStatus.NOT_FOUND); - } - - Participant participant = this.sessionManager.getParticipant(participantPrivateId); - if (participant.isIpcam()) { - return new ResponseEntity<>(HttpStatus.METHOD_NOT_ALLOWED); - } - - this.sessionManager.unpublishStream(session, streamId, null, null, EndReason.forceUnpublishByServer); - return new ResponseEntity<>(HttpStatus.NO_CONTENT); - } else { - return new ResponseEntity<>(HttpStatus.NOT_FOUND); - } - } - - @RequestMapping(value = "/tokens", method = RequestMethod.POST) - public ResponseEntity newToken(@RequestBody Map params) { - - if (params == null) { - return this.generateErrorResponse("Error in body parameters. Cannot be empty", "/tokens", - HttpStatus.BAD_REQUEST); - } - - log.info("REST API: POST {}/tokens {}", RequestMappings.API, params.toString()); - - String sessionId; - String roleString; - String metadata; - Boolean record; - try { - sessionId = (String) params.get("session"); - roleString = (String) params.get("role"); - metadata = (String) params.get("data"); - record = (Boolean) params.get("record"); - } catch (ClassCastException e) { - return this.generateErrorResponse("Type error in some parameter", "/tokens", HttpStatus.BAD_REQUEST); - } - - if (sessionId == null) { - return this.generateErrorResponse("\"session\" parameter is mandatory", "/tokens", HttpStatus.BAD_REQUEST); - } - - final Session session = this.sessionManager.getSessionWithNotActive(sessionId); - if (session == null) { - return this.generateErrorResponse("Session " + sessionId + " not found", "/tokens", HttpStatus.NOT_FOUND); - } - - JsonObject kurentoOptions = null; - - if (params.get("kurentoOptions") != null) { - try { - kurentoOptions = JsonParser.parseString(params.get("kurentoOptions").toString()).getAsJsonObject(); - } catch (Exception e) { - return this.generateErrorResponse("Error in parameter 'kurentoOptions'. It is not a valid JSON object", - "/tokens", HttpStatus.BAD_REQUEST); - } - } - - OpenViduRole role; - try { - if (roleString != null) { - role = OpenViduRole.valueOf(roleString); - } else { - role = OpenViduRole.PUBLISHER; - } - } catch (IllegalArgumentException e) { - return this.generateErrorResponse("Parameter role " + params.get("role") + " is not defined", "/tokens", - HttpStatus.BAD_REQUEST); - } - - KurentoTokenOptions kurentoTokenOptions = null; - if (kurentoOptions != null) { - try { - kurentoTokenOptions = new KurentoTokenOptions(kurentoOptions); - } catch (Exception e) { - return this.generateErrorResponse("Type error in some parameter of 'kurentoOptions'", "/tokens", - HttpStatus.BAD_REQUEST); - } - } - - metadata = (metadata != null) ? metadata : ""; - record = (record != null) ? record : true; - - // While closing a session tokens can't be generated - if (session.closingLock.readLock().tryLock()) { - try { - Token token = sessionManager.newToken(session, role, metadata, record, kurentoTokenOptions); - return new ResponseEntity<>(token.toJson().toString(), RestUtils.getResponseHeaders(), HttpStatus.OK); - } catch (Exception e) { - return this.generateErrorResponse( - "Error generating token for session " + sessionId + ": " + e.getMessage(), "/tokens", - HttpStatus.INTERNAL_SERVER_ERROR); - } finally { - session.closingLock.readLock().unlock(); - } - } else { - log.error("Session {} is in the process of closing. Token couldn't be generated", sessionId); - return this.generateErrorResponse("Session " + sessionId + " not found", "/tokens", HttpStatus.NOT_FOUND); - } - } - @RequestMapping(value = "/recordings/start", method = RequestMethod.POST) - public ResponseEntity startRecordingSession(@RequestBody Map params) { + public ResponseEntity startRecording(@RequestBody Map params) { if (params == null) { return this.generateErrorResponse("Error in body parameters. Cannot be empty", "/recordings/start", @@ -568,7 +542,7 @@ public class SessionRestController { } @RequestMapping(value = "/recordings/stop/{recordingId}", method = RequestMethod.POST) - public ResponseEntity stopRecordingSession(@PathVariable("recordingId") String recordingId) { + public ResponseEntity stopRecording(@PathVariable("recordingId") String recordingId) { log.info("REST API: POST {}/recordings/stop/{}", RequestMappings.API, recordingId); @@ -632,7 +606,7 @@ public class SessionRestController { } @RequestMapping(value = "/recordings", method = RequestMethod.GET) - public ResponseEntity getAllRecordings() { + public ResponseEntity listRecordings() { log.info("REST API: GET {}/recordings", RequestMappings.API); @@ -669,6 +643,87 @@ public class SessionRestController { return new ResponseEntity<>(this.recordingManager.deleteRecordingFromHost(recordingId, false)); } + @RequestMapping(value = "/tokens", method = RequestMethod.POST) + public ResponseEntity newToken(@RequestBody Map params) { + + if (params == null) { + return this.generateErrorResponse("Error in body parameters. Cannot be empty", "/tokens", + HttpStatus.BAD_REQUEST); + } + + log.info("REST API: POST {}/tokens {}", RequestMappings.API, params.toString()); + + String sessionId; + String metadata; + Boolean record; + try { + sessionId = (String) params.get("session"); + metadata = (String) params.get("data"); + record = (Boolean) params.get("record"); + } catch (ClassCastException e) { + return this.generateErrorResponse("Type error in some parameter", "/tokens", HttpStatus.BAD_REQUEST); + } + + if (sessionId == null) { + return this.generateErrorResponse("\"session\" parameter is mandatory", "/tokens", HttpStatus.BAD_REQUEST); + } + + log.warn("Token API is deprecated. Use Connection API instead (POST {}/sessions/{}/connection)", + RequestMappings.API, sessionId); + + final Session session = this.sessionManager.getSessionWithNotActive(sessionId); + if (session == null) { + return this.generateErrorResponse("Session " + sessionId + " not found", "/tokens", HttpStatus.NOT_FOUND); + } + Map map = new HashMap<>(); + params.entrySet().forEach(entry -> map.put((String) entry.getKey(), entry.getValue())); + map.put("type", "WEBRTC"); + ResponseEntity entity = this.newWebrtcConnection(session, metadata, record, params); + JsonObject jsonResponse = JsonParser.parseString(entity.getBody().toString()).getAsJsonObject(); + + if (jsonResponse.has("error")) { + return this.generateErrorResponse(jsonResponse.get("message").getAsString(), "/tokens", + HttpStatus.valueOf(jsonResponse.get("status").getAsInt())); + } else { + String connectionId = jsonResponse.get("id").getAsString(); + Token token = getTokenFromConnectionId(connectionId, session.getTokenIterator()); + return new ResponseEntity<>(token.toJson().toString(), RestUtils.getResponseHeaders(), HttpStatus.OK); + } + } + + @RequestMapping(value = "/sessions/{sessionId}/stream/{streamId}", method = RequestMethod.DELETE) + public ResponseEntity unpublishStream(@PathVariable("sessionId") String sessionId, + @PathVariable("streamId") String streamId) { + + log.info("REST API: DELETE {}/sessions/{}/stream/{}", RequestMappings.API, sessionId, streamId); + + Session session = this.sessionManager.getSessionWithNotActive(sessionId); + if (session == null) { + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } + + session = this.sessionManager.getSession(sessionId); + if (session != null) { + + final String participantPrivateId = this.sessionManager.getParticipantPrivateIdFromStreamId(sessionId, + streamId); + + if (participantPrivateId == null) { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + + Participant participant = this.sessionManager.getParticipant(participantPrivateId); + if (participant.isIpcam()) { + return new ResponseEntity<>(HttpStatus.METHOD_NOT_ALLOWED); + } + + this.sessionManager.unpublishStream(session, streamId, null, null, EndReason.forceUnpublishByServer); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); + } else { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } + } + @RequestMapping(value = "/signal", method = RequestMethod.POST) public ResponseEntity signal(@RequestBody Map params) { @@ -738,99 +793,108 @@ public class SessionRestController { return new ResponseEntity<>(HttpStatus.OK); } - @RequestMapping(value = "/sessions/{sessionId}/connection/{connectionId}", method = RequestMethod.GET) - public ResponseEntity getConnection(@PathVariable("sessionId") String sessionId, - @PathVariable("connectionId") String connectionId) { + protected ResponseEntity newWebrtcConnection(Session session, String serverData, Boolean record, + Map params) { - log.info("REST API: GET {}/sessions/{}/connection/{}", RequestMappings.API, sessionId, connectionId); + final String REQUEST_PATH = RequestMappings.API + "/sessions/" + session.getSessionId() + "/connection"; - Session session = this.sessionManager.getSessionWithNotActive(sessionId); - if (session != null) { - Participant p = session.getParticipantByPublicId(connectionId); - if (p != null) { - return new ResponseEntity<>(p.toJson().toString(), RestUtils.getResponseHeaders(), HttpStatus.OK); + String roleString = null; + try { + roleString = (String) params.get("role"); + } catch (ClassCastException e) { + return this.generateErrorResponse("Type error in some parameter", REQUEST_PATH, HttpStatus.BAD_REQUEST); + } + + OpenViduRole role = null; + try { + if (roleString != null) { + role = OpenViduRole.valueOf(roleString); } else { - Token t = getTokenFromConnectionId(connectionId, session.getTokenIterator()); - if (t != null) { - return new ResponseEntity<>(t.toJsonAsParticipant().toString(), RestUtils.getResponseHeaders(), - HttpStatus.OK); - } else { - return new ResponseEntity<>(HttpStatus.NOT_FOUND); - } + role = OpenViduRole.PUBLISHER; + } + } catch (IllegalArgumentException e) { + return this.generateErrorResponse("Parameter role " + params.get("role") + " is not defined", REQUEST_PATH, + HttpStatus.BAD_REQUEST); + } + + JsonObject kurentoOptions = null; + if (params.get("kurentoOptions") != null) { + try { + kurentoOptions = JsonParser.parseString(params.get("kurentoOptions").toString()).getAsJsonObject(); + } catch (Exception e) { + return this.generateErrorResponse("Error in parameter 'kurentoOptions'. It is not a valid JSON object", + REQUEST_PATH, HttpStatus.BAD_REQUEST); + } + } + + KurentoTokenOptions kurentoTokenOptions = null; + if (kurentoOptions != null) { + try { + kurentoTokenOptions = new KurentoTokenOptions(kurentoOptions); + } catch (Exception e) { + return this.generateErrorResponse("Type error in some parameter of 'kurentoOptions'", REQUEST_PATH, + HttpStatus.BAD_REQUEST); + } + } + + serverData = (serverData != null) ? serverData : ""; + record = (record != null) ? record : true; + + // While closing a session tokens can't be generated + if (session.closingLock.readLock().tryLock()) { + try { + Token token = sessionManager.newToken(session, role, serverData, record, kurentoTokenOptions); + return new ResponseEntity<>(token.toJsonAsParticipant().toString(), RestUtils.getResponseHeaders(), + HttpStatus.OK); + } catch (Exception e) { + return this.generateErrorResponse( + "Error creating Connection for session " + session.getSessionId() + ": " + e.getMessage(), + REQUEST_PATH, HttpStatus.INTERNAL_SERVER_ERROR); + } finally { + session.closingLock.readLock().unlock(); } } else { - return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + log.error("Session {} is in the process of closing. Connection couldn't be created", + session.getSessionId()); + return this.generateErrorResponse("Session " + session.getSessionId() + " not found", REQUEST_PATH, + HttpStatus.NOT_FOUND); } } - @RequestMapping(value = "/sessions/{sessionId}/connection", method = RequestMethod.GET) - public ResponseEntity listConnections(@PathVariable("sessionId") String sessionId, - @RequestParam(value = "pendingConnections", defaultValue = "true", required = false) boolean pendingConnections, - @RequestParam(value = "webRtcStats", defaultValue = "false", required = false) boolean webRtcStats) { + protected ResponseEntity newIpcamConnection(Session session, String serverData, Boolean record, + Map params) { - log.info("REST API: GET {}/sessions/{}/connection", RequestMappings.API, sessionId); + final String REQUEST_PATH = RequestMappings.API + "/sessions/" + session.getSessionId() + "/connection"; - Session session = this.sessionManager.getSessionWithNotActive(sessionId); - - if (session != null) { - JsonObject json = new JsonObject(); - JsonArray jsonArray = session.getSnapshotOfConnectionsAsJsonArray(pendingConnections, webRtcStats); - json.addProperty("numberOfElements", jsonArray.size()); - json.add("content", jsonArray); - return new ResponseEntity<>(json.toString(), RestUtils.getResponseHeaders(), HttpStatus.OK); - } else { - return new ResponseEntity<>(HttpStatus.NOT_FOUND); - } - } - - @RequestMapping(value = "/sessions/{sessionId}/connection", method = RequestMethod.POST) - public ResponseEntity publishIpcam(@PathVariable("sessionId") String sessionId, @RequestBody Map params) { - - if (params == null) { - return this.generateErrorResponse("Error in body parameters. Cannot be empty", - "/sessions/" + sessionId + "/connection", HttpStatus.BAD_REQUEST); - } - - log.info("REST API: POST {}/sessions/{}/connection {}", RequestMappings.API, sessionId, params.toString()); - - Session session = this.sessionManager.getSessionWithNotActive(sessionId); - if (session == null) { - return new ResponseEntity<>(HttpStatus.NOT_FOUND); - } - - String type; String rtspUri; Boolean adaptativeBitrate; Boolean onlyPlayWithSubscribers; Integer networkCache; - String data; try { - type = (String) params.get("type"); rtspUri = (String) params.get("rtspUri"); adaptativeBitrate = (Boolean) params.get("adaptativeBitrate"); onlyPlayWithSubscribers = (Boolean) params.get("onlyPlayWithSubscribers"); networkCache = (Integer) params.get("networkCache"); - data = (String) params.get("data"); } catch (ClassCastException e) { - return this.generateErrorResponse("Type error in some parameter", "/sessions/" + sessionId + "/connection", - HttpStatus.BAD_REQUEST); - } - if (rtspUri == null) { - return this.generateErrorResponse("\"rtspUri\" parameter is mandatory", - "/sessions/" + sessionId + "/connection", HttpStatus.BAD_REQUEST); + return this.generateErrorResponse("Type error in some parameter", REQUEST_PATH, HttpStatus.BAD_REQUEST); + } + + if (rtspUri == null) { + return this.generateErrorResponse("\"rtspUri\" parameter is mandatory", REQUEST_PATH, + HttpStatus.BAD_REQUEST); } - type = "IPCAM"; // Other possible values in the future adaptativeBitrate = adaptativeBitrate != null ? adaptativeBitrate : true; onlyPlayWithSubscribers = onlyPlayWithSubscribers != null ? onlyPlayWithSubscribers : true; networkCache = networkCache != null ? networkCache : 2000; - data = data != null ? data : ""; + serverData = serverData != null ? serverData : ""; + record = (record != null) ? record : true; boolean hasAudio = true; boolean hasVideo = true; boolean audioActive = true; boolean videoActive = true; - String typeOfVideo = type; + String typeOfVideo = ConnectionType.IPCAM.name(); Integer frameRate = null; String videoDimensions = null; KurentoMediaOptions mediaOptions = new KurentoMediaOptions(true, null, hasAudio, hasVideo, audioActive, @@ -843,15 +907,14 @@ public class SessionRestController { if (session.isClosed()) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } - Participant ipcamParticipant = this.sessionManager.publishIpcam(session, mediaOptions, data); + Participant ipcamParticipant = this.sessionManager.publishIpcam(session, mediaOptions, serverData); return new ResponseEntity<>(ipcamParticipant.toJson().toString(), RestUtils.getResponseHeaders(), HttpStatus.OK); } catch (MalformedURLException e) { - return this.generateErrorResponse("\"rtspUri\" parameter is not a valid rtsp uri", - "/sessions/" + sessionId + "/connection", HttpStatus.BAD_REQUEST); + return this.generateErrorResponse("\"rtspUri\" parameter is not a valid rtsp uri", REQUEST_PATH, + HttpStatus.BAD_REQUEST); } catch (Exception e) { - return this.generateErrorResponse(e.getMessage(), "/sessions/" + sessionId + "/connection", - HttpStatus.INTERNAL_SERVER_ERROR); + return this.generateErrorResponse(e.getMessage(), REQUEST_PATH, HttpStatus.INTERNAL_SERVER_ERROR); } finally { session.closingLock.readLock().unlock(); } diff --git a/openvidu-server/src/test/java/io/openvidu/server/test/integration/SessionGarbageCollectorIntegrationTest.java b/openvidu-server/src/test/java/io/openvidu/server/test/integration/SessionGarbageCollectorIntegrationTest.java index e4abf225..125c1ea3 100644 --- a/openvidu-server/src/test/java/io/openvidu/server/test/integration/SessionGarbageCollectorIntegrationTest.java +++ b/openvidu-server/src/test/java/io/openvidu/server/test/integration/SessionGarbageCollectorIntegrationTest.java @@ -98,7 +98,7 @@ public class SessionGarbageCollectorIntegrationTest { } private String getSessionId() { - String stringResponse = (String) sessionRestController.getSessionId(new HashMap<>()).getBody(); + String stringResponse = (String) sessionRestController.initializeSession(new HashMap<>()).getBody(); return new Gson().fromJson(stringResponse, JsonObject.class).get("id").getAsString(); } diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduProTestAppE2eTest.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduProTestAppE2eTest.java index 81c35c33..84957007 100644 --- a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduProTestAppE2eTest.java +++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduProTestAppE2eTest.java @@ -212,14 +212,16 @@ public class OpenViduProTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { // Updating only role should let record value untouched restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + tokenConnectionId, "{'role':'MODERATOR'}", HttpStatus.SC_OK, true, true, true, - "{'id':'" + tokenConnectionId + "','object':'connection','status':'pending','connectionId':'" + "{'id':'" + tokenConnectionId + + "','object':'connection','type':'WEBRTC','status':'pending','connectionId':'" + tokenConnectionId + "','role':'MODERATOR','record':false,'token':'" + token + "','sessionId':'CUSTOM_SESSION_ID','serverData':'','publishers':null,'subscribers':null,'createdAt':" + createdAt + ",'activeAt':null,'platform':null,'location':null,'clientData':null}"); // Updating only record should let role value untouched restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + tokenConnectionId, "{'record':true}", HttpStatus.SC_OK, true, true, true, - "{'id':'" + tokenConnectionId + "','object':'connection','status':'pending','connectionId':'" + "{'id':'" + tokenConnectionId + + "','object':'connection','type':'WEBRTC','status':'pending','connectionId':'" + tokenConnectionId + "','role':'MODERATOR','record':true,'token':'" + token + "','sessionId':'CUSTOM_SESSION_ID','serverData':'','publishers':null,'subscribers':null,'createdAt':" + createdAt + ",'activeAt':null,'platform':null,'location':null,'clientData':null}"); @@ -282,23 +284,28 @@ public class OpenViduProTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { // Updating only role should let record value untouched restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + tokenConnectionId, "{'role':'MODERATOR'}", HttpStatus.SC_OK, false, true, true, - "{'id':'" + tokenConnectionId + "','object':'connection','status':'active','connectionId':'" + "{'id':'" + tokenConnectionId + + "','object':'connection','type':'WEBRTC','status':'active','connectionId':'" + tokenConnectionId + "','role':'MODERATOR','record':false,'token':'" + token + "','sessionId':'CUSTOM_SESSION_ID','serverData':'','publishers':[],'subscribers':[]}"); // Updating only record should let role value untouched restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + tokenConnectionId, "{'record':true}", HttpStatus.SC_OK, false, true, true, - "{'id':'" + tokenConnectionId + "','object':'connection','status':'active','connectionId':'" + "{'id':'" + tokenConnectionId + + "','object':'connection','type':'WEBRTC','status':'active','connectionId':'" + tokenConnectionId + "','role':'MODERATOR','record':true,'token':'" + token + "','sessionId':'CUSTOM_SESSION_ID','serverData':'','publishers':[],'subscribers':[]}"); restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + tokenConnectionId, "{'role':'SUBSCRIBER','record':true,'data':'OTHER DATA'}", HttpStatus.SC_OK, false, true, true, - "{'id':'" + tokenConnectionId + "','object':'connection','status':'active','connectionId':'" + "{'id':'" + tokenConnectionId + + "','object':'connection','type':'WEBRTC','status':'active','connectionId':'" + tokenConnectionId + "','role':'SUBSCRIBER','record':true,'token':'" + token + "','sessionId':'CUSTOM_SESSION_ID','serverData':'','publishers':[],'subscribers':[]}"); restClient.rest(HttpMethod.PATCH, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + tokenConnectionId, - "{'role':'PUBLISHER'}", HttpStatus.SC_OK, false, true, true, - "{'id':'" + tokenConnectionId + "','object':'connection','status':'active','connectionId':'" + "{'role':'PUBLISHER'}", HttpStatus.SC_OK, + false, true, true, + "{'id':'" + tokenConnectionId + + "','object':'connection','type':'WEBRTC','status':'active','connectionId':'" + tokenConnectionId + "','role':'PUBLISHER','record':true,'token':'" + token + "','sessionId':'CUSTOM_SESSION_ID','serverData':'','publishers':[],'subscribers':[]}"); diff --git a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java index ba61abe0..eee638ac 100644 --- a/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java +++ b/openvidu-test-e2e/src/test/java/io/openvidu/test/e2e/OpenViduTestAppE2eTest.java @@ -86,7 +86,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { final String DEFAULT_JSON_SESSION = "{'id':'STR','object':'STR','sessionId':'STR','createdAt':0,'mediaMode':'STR','recordingMode':'STR','defaultOutputMode':'STR','defaultRecordingLayout':'STR','customSessionId':'STR','connections':{'numberOfElements':0,'content':[]},'recording':false}"; final String DEFAULT_JSON_TOKEN = "{'id':'STR','object':'STR','token':'STR','connectionId':0,'session':'STR','createdAt':0,'role':'STR','data':'STR','record':true}"; - final String DEFAULT_JSON_CONNECTION = "{'id':'STR','object':'STR','status':'STR','connectionId':'STR','sessionId':'STR','createdAt':0,'activeAt':0,'location':'STR','platform':'STR','role':'STR','record':true,'serverData':'STR','clientData':'STR','publishers':[],'subscribers':[]}"; + final String DEFAULT_JSON_CONNECTION = "{'id':'STR','object':'STR','type':'STR','status':'STR','connectionId':'STR','sessionId':'STR','createdAt':0,'activeAt':0,'location':'STR','platform':'STR','role':'STR','record':true,'serverData':'STR','clientData':'STR','publishers':[],'subscribers':[]}"; @BeforeAll() protected static void setupAll() { @@ -2647,7 +2647,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId1, null, HttpStatus.SC_OK, true, true, true, "{'id':'" + connectionId1 + "','connectionId':'" + connectionId1 - + "','object':'connection','status':'pending','sessionId':'CUSTOM_SESSION_ID','token':'" + + "','object':'connection','type':'WEBRTC','status':'pending','sessionId':'CUSTOM_SESSION_ID','token':'" + token1 + "','role':'MODERATOR','serverData':'SERVER_DATA','record':true,'createdAt':" + createdAt1 + ",'activeAt':null,'platform':null,'location':null,'clientData':null,'publishers':null,'subscribers':null}"); @@ -2744,13 +2744,13 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId1, null, HttpStatus.SC_OK, false, true, false, "{'id':'" + connectionId1 + "','connectionId':'" + connectionId1 - + "','object':'connection','status':'active','sessionId':'CUSTOM_SESSION_ID','token':'" + token1 - + "','role':'MODERATOR','serverData':'SERVER_DATA','record':true}"); + + "','object':'connection','type':'WEBRTC','status':'active','sessionId':'CUSTOM_SESSION_ID','token':'" + + token1 + "','role':'MODERATOR','serverData':'SERVER_DATA','record':true}"); restClient.rest(HttpMethod.GET, "/openvidu/api/sessions/CUSTOM_SESSION_ID/connection/" + connectionId2, null, HttpStatus.SC_OK, false, true, false, "{'id':'" + connectionId2 + "','connectionId':'" + connectionId2 - + "','object':'connection','status':'active','sessionId':'CUSTOM_SESSION_ID','token':'" + token2 - + "','role':'PUBLISHER','serverData':'','record':true}"); + + "','object':'connection','type':'WEBRTC','status':'active','sessionId':'CUSTOM_SESSION_ID','token':'" + + token2 + "','role':'PUBLISHER','serverData':'','record':true}"); /** GET /openvidu/api/recordings (before recording started) **/ restClient.rest(HttpMethod.GET, "/openvidu/api/recordings/NOT_EXISTS", HttpStatus.SC_NOT_FOUND); @@ -3291,6 +3291,7 @@ public class OpenViduTestAppE2eTest extends AbstractOpenViduTestAppE2eTest { Assert.assertEquals("Wrong serverData property", "MY_IP_CAMERA", response.get("serverData").getAsString()); Assert.assertEquals("Wrong platform property", "IPCAM", response.get("platform").getAsString()); Assert.assertEquals("Wrong role property", "PUBLISHER", response.get("role").getAsString()); + Assert.assertEquals("Wrong type property", "IPCAM", response.get("type").getAsString()); Assert.assertEquals("Wrong number of publishers in IPCAM participant", 1, response.get("publishers").getAsJsonArray().size());