Merge branch 'v2.6.x-release' of https://github.com/bigbluebutton/bigbluebutton into tldraw-recording

This commit is contained in:
germanocaumo 2022-06-30 14:31:08 +00:00
commit c2db91b5f9
148 changed files with 388 additions and 6087 deletions

View File

@ -1,28 +0,0 @@
dist: focal
language: node_js
node_js: 16
env:
- JOB_TYPE=linter
- JOB_TYPE=acceptance_tests
global:
- BBB_SERVER_URL=http://localhost/bigbluebutton/api
jobs:
allow_failures:
- env: JOB_TYPE=linter
include:
- stage: "Linter"
name: "ESLint"
env: JOB_TYPE=linter
- stage: "Tests"
name: "Acceptance Tests"
env: JOB_TYPE=acceptance_tests
script:
- travis_wait 30 bash ./build_script.sh $JOB_TYPE
after_script:
- docker stop $docker
- docker rm $docker
- docker rmi b2

View File

@ -75,6 +75,12 @@ object LockSettingsUtil {
}
}
def isMicrophoneSharingLocked(user: UserState, liveMeeting: LiveMeeting): Boolean = {
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)
user.role == Roles.VIEWER_ROLE && user.locked && permissions.disableMic
}
def isCameraBroadcastLocked(user: UserState, liveMeeting: LiveMeeting): Boolean = {
val permissions = MeetingStatus2x.getPermissions(liveMeeting.status)

View File

@ -70,11 +70,6 @@ trait SystemConfiguration {
lazy val fromBbbWebRedisChannel = Try(config.getString("redis.fromBbbWebRedisChannel")).getOrElse("from-bbb-web-redis-channel")
lazy val toAkkaTranscodeRedisChannel = Try(config.getString("redis.toAkkaTranscodeRedisChannel")).getOrElse("bigbluebutton:to-bbb-transcode:system")
lazy val fromAkkaTranscodeRedisChannel = Try(config.getString("redis.fromAkkaTranscodeRedisChannel")).getOrElse("bigbluebutton:from-bbb-transcode:system")
lazy val toAkkaTranscodeJsonChannel = Try(config.getString("eventBus.toAkkaTranscodeJsonChannel")).getOrElse("to-akka-transcode-json-channel")
lazy val fromAkkaTranscodeJsonChannel = Try(config.getString("eventBus.fromAkkaTranscodeJsonChannel")).getOrElse("from-akka-transcode-json-channel")
lazy val analyticsIncludeChat = Try(config.getBoolean("analytics.includeChat")).getOrElse(true)
// Grab the "interface" parameter from the http config

View File

@ -187,9 +187,6 @@ class BigBlueButtonActor(
val disconnectEvnt = MsgBuilder.buildDisconnectAllClientsSysMsg(msg.meetingId, "meeting-destroyed")
m2.outMsgRouter.send(disconnectEvnt)
val stopTranscodersCmd = MsgBuilder.buildStopMeetingTranscodersSysCmdMsg(msg.meetingId)
m2.outMsgRouter.send(stopTranscodersCmd)
log.info("Destroyed meetingId={}", msg.meetingId)
val destroyedEvent = MsgBuilder.buildMeetingDestroyedEvtMsg(msg.meetingId)
m2.outMsgRouter.send(destroyedEvent)

View File

@ -25,8 +25,7 @@ trait GetScreenBroadcastPermissionReqMsgHdlr {
val meetingId = liveMeeting.props.meetingProp.intId
val reason = "No permission to share the screen."
PermissionCheck.ejectUserForFailedPermission(meetingId, msg.header.userId, reason, outGW, liveMeeting)
} else if (!user.userLeftFlag.left
&& liveMeeting.props.meetingProp.intId == msg.body.meetingId
} else if (liveMeeting.props.meetingProp.intId == msg.body.meetingId
&& liveMeeting.props.voiceProp.voiceConf == msg.body.voiceConf) {
allowed = true
}

View File

@ -17,8 +17,7 @@ trait GetScreenSubscribePermissionReqMsgHdlr {
for {
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
} yield {
if (!user.userLeftFlag.left
&& liveMeeting.props.meetingProp.intId == msg.body.meetingId
if (liveMeeting.props.meetingProp.intId == msg.body.meetingId
&& liveMeeting.props.voiceProp.voiceConf == msg.body.voiceConf
&& ScreenshareModel.getRTMPBroadcastingUrl(liveMeeting.screenshareModel) == msg.body.streamId) {
allowed = true

View File

@ -1,35 +1,51 @@
package org.bigbluebutton.core2.message.handlers
package org.bigbluebutton.core.apps.voice
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.running.{ MeetingActor, OutMsgRouter }
import org.bigbluebutton.core.models.Users2x
import org.bigbluebutton.core2.message.senders.MsgBuilder
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
trait GetGlobalAudioPermissionReqMsgHdlr {
this: MeetingActor =>
val outGW: OutMsgRouter
def handleGetGlobalAudioPermissionReqMsg(msg: GetGlobalAudioPermissionReqMsg) {
var allowed = false
def handleGetGlobalAudioPermissionReqMsg(msg: GetGlobalAudioPermissionReqMsg): Unit = {
for {
user <- Users2x.findWithIntId(liveMeeting.users2x, msg.body.userId)
} yield {
if (!user.userLeftFlag.left
&& liveMeeting.props.meetingProp.intId == msg.body.meetingId
&& liveMeeting.props.voiceProp.voiceConf == msg.body.voiceConf) {
allowed = true
}
def broadcastEvent(
meetingId: String,
voiceConf: String,
userId: String,
sfuSessionId: String,
allowed: Boolean
): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(GetGlobalAudioPermissionRespMsg.NAME, routing)
val header = BbbClientMsgHeader(GetGlobalAudioPermissionRespMsg.NAME, meetingId, userId)
val body = GetGlobalAudioPermissionRespMsgBody(
meetingId,
voiceConf,
userId,
sfuSessionId,
allowed
)
val event = GetGlobalAudioPermissionRespMsg(header, body)
val eventMsg = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(eventMsg)
}
val event = MsgBuilder.buildGetGlobalAudioPermissionRespMsg(
val allowed = VoiceHdlrHelpers.isGlobalAudioSubscribeAllowed(
liveMeeting,
msg.body.meetingId,
msg.body.userId,
msg.body.voiceConf
)
broadcastEvent(
liveMeeting.props.meetingProp.intId,
liveMeeting.props.voiceProp.voiceConf,
msg.body.userId,
msg.body.sfuSessionId,
allowed
)
outGW.send(event)
}
}

View File

@ -0,0 +1,53 @@
package org.bigbluebutton.core.apps.voice
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.running.{ LiveMeeting, MeetingActor, OutMsgRouter }
trait GetMicrophonePermissionReqMsgHdlr {
this: MeetingActor =>
val liveMeeting: LiveMeeting
val outGW: OutMsgRouter
def handleGetMicrophonePermissionReqMsg(msg: GetMicrophonePermissionReqMsg): Unit = {
def broadcastEvent(
meetingId: String,
voiceConf: String,
userId: String,
sfuSessionId: String,
allowed: Boolean
): Unit = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(GetMicrophonePermissionRespMsg.NAME, routing)
val header = BbbClientMsgHeader(GetMicrophonePermissionRespMsg.NAME, meetingId, userId)
val body = GetMicrophonePermissionRespMsgBody(
meetingId,
voiceConf,
userId,
sfuSessionId,
allowed
)
val event = GetMicrophonePermissionRespMsg(header, body)
val eventMsg = BbbCommonEnvCoreMsg(envelope, event)
outGW.send(eventMsg)
}
val allowed = VoiceHdlrHelpers.isMicrophoneSharingAllowed(
liveMeeting,
msg.body.meetingId,
msg.body.userId,
msg.body.voiceConf,
msg.body.callerIdNum
)
broadcastEvent(
liveMeeting.props.meetingProp.intId,
liveMeeting.props.voiceProp.voiceConf,
msg.body.userId,
msg.body.sfuSessionId,
allowed
)
}
}

View File

@ -0,0 +1,53 @@
package org.bigbluebutton.core.apps.voice
import org.bigbluebutton.common2.msgs._
import org.bigbluebutton.core.models.{ Users2x, VoiceUsers }
import org.bigbluebutton.core.running.{ LiveMeeting }
import org.bigbluebutton.LockSettingsUtil
import org.bigbluebutton.SystemConfiguration
object VoiceHdlrHelpers extends SystemConfiguration {
def isGlobalAudioSubscribeAllowed(
liveMeeting: LiveMeeting,
meetingId: String,
userId: String,
voiceConf: String
): Boolean = {
Users2x.findWithIntId(liveMeeting.users2x, userId) match {
case Some(user) => (
applyPermissionCheck &&
liveMeeting.props.meetingProp.intId == meetingId &&
liveMeeting.props.voiceProp.voiceConf == voiceConf
)
case _ => false
}
}
def isMicrophoneSharingAllowed(
liveMeeting: LiveMeeting,
meetingId: String,
userId: String,
voiceConf: String,
callerIdNum: String
): Boolean = {
Users2x.findWithIntId(liveMeeting.users2x, userId) match {
case Some(user) => {
val microphoneSharingLocked = LockSettingsUtil.isMicrophoneSharingLocked(
user,
liveMeeting
)
val isCallerBanned = VoiceUsers.isCallerBanned(
callerIdNum,
liveMeeting.voiceUsers
)
(applyPermissionCheck &&
!isCallerBanned &&
!microphoneSharingLocked &&
liveMeeting.props.meetingProp.intId == meetingId &&
liveMeeting.props.voiceProp.voiceConf == voiceConf)
}
case _ => false
}
}
}

View File

@ -23,7 +23,6 @@ object CameraHdlrHelpers extends SystemConfiguration with RightsManagementTrait
(applyPermissionCheck &&
!camBroadcastLocked &&
!camCapReached &&
!user.userLeftFlag.left &&
streamId.startsWith(user.intId) &&
liveMeeting.props.meetingProp.intId == meetingId)
}
@ -43,7 +42,6 @@ object CameraHdlrHelpers extends SystemConfiguration with RightsManagementTrait
(applyPermissionCheck &&
!camSubscribeLocked &&
!user.userLeftFlag.left &&
liveMeeting.props.meetingProp.intId == meetingId)
}
case _ => false

View File

@ -215,6 +215,8 @@ class ReceivedJsonMsgHandlerActor(
routeVoiceMsg[VoiceConfCallStateEvtMsg](envelope, jsonNode)
case GetGlobalAudioPermissionReqMsg.NAME =>
routeGenericMsg[GetGlobalAudioPermissionReqMsg](envelope, jsonNode)
case GetMicrophonePermissionReqMsg.NAME =>
routeGenericMsg[GetMicrophonePermissionReqMsg](envelope, jsonNode)
// Breakout rooms
case BreakoutRoomsListMsg.NAME =>

View File

@ -80,6 +80,7 @@ class MeetingActor(
with MuteMeetingCmdMsgHdlr
with IsMeetingMutedReqMsgHdlr
with GetGlobalAudioPermissionReqMsgHdlr
with GetMicrophonePermissionReqMsgHdlr
with GetScreenBroadcastPermissionReqMsgHdlr
with GetScreenSubscribePermissionReqMsgHdlr
@ -467,6 +468,8 @@ class MeetingActor(
handleUserStatusVoiceConfEvtMsg(m)
case m: GetGlobalAudioPermissionReqMsg =>
handleGetGlobalAudioPermissionReqMsg(m)
case m: GetMicrophonePermissionReqMsg =>
handleGetMicrophonePermissionReqMsg(m)
// Layout
case m: GetCurrentLayoutReqMsg => handleGetCurrentLayoutReqMsg(m)

View File

@ -347,16 +347,6 @@ object MsgBuilder {
BbbCommonEnvCoreMsg(envelope, event)
}
def buildStopMeetingTranscodersSysCmdMsg(meetingId: String): BbbCommonEnvCoreMsg = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(StopMeetingTranscodersSysCmdMsg.NAME, routing)
val body = StopMeetingTranscodersSysCmdMsgBody()
val header = BbbCoreHeaderWithMeetingId(StopMeetingTranscodersSysCmdMsg.NAME, meetingId)
val event = StopMeetingTranscodersSysCmdMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildRecordingChapterBreakSysMsg(meetingId: String, timestamp: Long): BbbCommonEnvCoreMsg = {
val routing = collection.immutable.HashMap("sender" -> "bbb-apps-akka")
val envelope = BbbCoreEnvelope(RecordingChapterBreakSysMsg.NAME, routing)
@ -461,23 +451,6 @@ object MsgBuilder {
BbbCommonEnvCoreMsg(envelope, event)
}
def buildGetGlobalAudioPermissionRespMsg(
meetingId: String,
voiceConf: String,
userId: String,
sfuSessionId: String,
allowed: Boolean
): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.DIRECT, meetingId, userId)
val envelope = BbbCoreEnvelope(GetGlobalAudioPermissionRespMsg.NAME, routing)
val header = BbbClientMsgHeader(GetGlobalAudioPermissionRespMsg.NAME, meetingId, userId)
val body = GetGlobalAudioPermissionRespMsgBody(meetingId, voiceConf, userId, sfuSessionId, allowed)
val event = GetGlobalAudioPermissionRespMsg(header, body)
BbbCommonEnvCoreMsg(envelope, event)
}
def buildMeetingTimeRemainingUpdateEvtMsg(meetingId: String, timeLeftInSec: Long, timeUpdatedInMinutes: Int = 0): BbbCommonEnvCoreMsg = {
val routing = Routing.addMsgToClientRouting(MessageTypes.BROADCAST_TO_MEETING, meetingId, "not-used")
val envelope = BbbCoreEnvelope(MeetingTimeRemainingUpdateEvtMsg.NAME, routing)

View File

@ -46,8 +46,8 @@ object Dependencies {
val apacheLang = "org.apache.commons" % "commons-lang3" % Versions.lang
val bbbCommons = "org.bigbluebutton" % "bbb-common-message_2.13" % Versions.bbbCommons excludeAll (
ExclusionRule(organization = "org.red5"))
val bbbCommons = "org.bigbluebutton" % "bbb-common-message_2.13" % Versions.bbbCommons
val bbbFseslClient = "org.bigbluebutton" % "bbb-fsesl-client" % Versions.bbbFsesl
}

View File

@ -49,7 +49,6 @@ public class MessageSender extends RedisAwareCommunicator {
connectionPool = ConnectionPoolSupport.createGenericObjectPool(() -> redisClient.connectPubSub(),
createPoolingConfig());
log.info("Redis org.bigbluebutton.red5.pubsub.message publisher starting!");
try {
sendMessage = true;
@ -84,7 +83,7 @@ public class MessageSender extends RedisAwareCommunicator {
RedisAsyncCommands<String, String> async = connection.async();
RedisFuture<Long> future = async.publish(channel, message);
} catch (Exception e) {
log.warn("Cannot publish the org.bigbluebutton.red5.pubsub.message to redis", e);
log.warn("Cannot publish the message to redis", e);
}
}
};

View File

@ -49,8 +49,6 @@ case class UsersProp(
case class MetadataProp(metadata: collection.immutable.Map[String, String])
case class ScreenshareProps(screenshareConf: String, red5ScreenshareIp: String, red5ScreenshareApp: String)
case class LockSettingsProps(
disableCam: Boolean,
disableMic: Boolean,

View File

@ -1,43 +0,0 @@
package org.bigbluebutton.common2.msgs
/* In Messages */
object StartProbingSysReqMsg { val NAME = "StartProbingSysReqMsg" }
case class StartProbingSysReqMsg(header: BbbCoreHeaderWithMeetingId, body: StartProbingSysReqMsgBody) extends BbbCoreMsg
case class StartProbingSysReqMsgBody(transcoderId: String, params: Map[String, String])
object StartTranscoderSysReqMsg { val NAME = "StartTranscoderSysReqMsg" }
case class StartTranscoderSysReqMsg(header: BbbCoreHeaderWithMeetingId, body: StartTranscoderSysReqMsgBody) extends BbbCoreMsg
case class StartTranscoderSysReqMsgBody(transcoderId: String, params: Map[String, String])
object StopTranscoderSysReqMsg { val NAME = "StopTranscoderSysReqMsg" }
case class StopTranscoderSysReqMsg(header: BbbCoreHeaderWithMeetingId, body: StopTranscoderSysReqMsgBody) extends BbbCoreMsg
case class StopTranscoderSysReqMsgBody(transcoderId: String)
object UpdateTranscoderSysReqMsg { val NAME = "UpdateTranscoderSysReqMsg" }
case class UpdateTranscoderSysReqMsg(header: BbbCoreHeaderWithMeetingId, body: UpdateTranscoderSysReqMsgBody) extends BbbCoreMsg
case class UpdateTranscoderSysReqMsgBody(transcoderId: String, params: Map[String, String])
object TranscoderStatusUpdateSysCmdMsg { val NAME = "TranscoderStatusUpdateSysCmdMsg" }
case class TranscoderStatusUpdateSysCmdMsg(header: BbbCoreHeaderWithMeetingId, body: TranscoderStatusUpdateSysCmdMsgBody) extends BbbCoreMsg
case class TranscoderStatusUpdateSysCmdMsgBody(transcoderId: String, params: Map[String, String])
object StopMeetingTranscodersSysCmdMsg { val NAME = "StopMeetingTranscodersSysCmdMsg" }
case class StopMeetingTranscodersSysCmdMsg(header: BbbCoreHeaderWithMeetingId, body: StopMeetingTranscodersSysCmdMsgBody) extends BbbCoreMsg
case class StopMeetingTranscodersSysCmdMsgBody()
/* Out Messages */
object StartProbingSysRespMsg { val NAME = "StartProbingSysRespMsg" }
case class StartProbingSysRespMsg(header: BbbCoreHeaderWithMeetingId, body: StartProbingSysRespMsgBody) extends BbbCoreMsg
case class StartProbingSysRespMsgBody(transcoderId: String, params: Map[String, String])
object StartTranscoderSysRespMsg { val NAME = "StartTranscoderSysRespMsg" }
case class StartTranscoderSysRespMsg(header: BbbCoreHeaderWithMeetingId, body: StartTranscoderSysRespMsgBody) extends BbbCoreMsg
case class StartTranscoderSysRespMsgBody(transcoderId: String, params: Map[String, String])
object StopTranscoderSysRespMsg { val NAME = "StopTranscoderSysRespMsg" }
case class StopTranscoderSysRespMsg(header: BbbCoreHeaderWithMeetingId, body: StopTranscoderSysRespMsgBody) extends BbbCoreMsg
case class StopTranscoderSysRespMsgBody(transcoderId: String)
object UpdateTranscoderSysRespMsg { val NAME = "UpdateTranscoderSysRespMsg" }
case class UpdateTranscoderSysRespMsg(header: BbbCoreHeaderWithMeetingId, body: UpdateTranscoderSysRespMsgBody) extends BbbCoreMsg
case class UpdateTranscoderSysRespMsgBody(transcoderId: String, params: Map[String, String])

View File

@ -310,7 +310,7 @@ case class UserJoinMeetingAfterReconnectReqMsg(header: BbbClientMsgHeader, body:
case class UserJoinMeetingAfterReconnectReqMsgBody(userId: String, authToken: String, clientType: String)
/**
* Sent from bbb-apps when user disconnects from Red5.
* Sent from client to bbb-akka to notify that a user is leaving
*/
object UserLeaveReqMsg { val NAME = "UserLeaveReqMsg" }
case class UserLeaveReqMsg(header: BbbClientMsgHeader, body: UserLeaveReqMsgBody) extends StandardMsg

View File

@ -561,3 +561,40 @@ case class GetGlobalAudioPermissionRespMsgBody(
sfuSessionId: String,
allowed: Boolean
)
/* Sent by bbb-webrtc-sfu to ask permission for a new microphone/full audio
* connection
* - callerIdNum: the session's callerId as assembled by the requester
* - sfuSessionId: the UUID for this request's session in bbb-webrtc-sfu.
* Used for response matching.
*/
object GetMicrophonePermissionReqMsg { val NAME = "GetMicrophonePermissionReqMsg" }
case class GetMicrophonePermissionReqMsg(
header: BbbClientMsgHeader,
body: GetMicrophonePermissionReqMsgBody
) extends StandardMsg
case class GetMicrophonePermissionReqMsgBody(
meetingId: String,
voiceConf: String,
userId: String,
callerIdNum: String,
sfuSessionId: String
)
/* Sent to bbb-webrtc-sfu as a response to GetMicrophonePermissionReqMsg
* - sfuSessionId: the UUID for this request's session in bbb-webrtc-sfu.
* Used for response matching.
* - allowed: whether session creation should be allowed.
*/
object GetMicrophonePermissionRespMsg { val NAME = "GetMicrophonePermissionRespMsg" }
case class GetMicrophonePermissionRespMsg(
header: BbbClientMsgHeader,
body: GetMicrophonePermissionRespMsgBody
) extends StandardMsg
case class GetMicrophonePermissionRespMsgBody(
meetingId: String,
voiceConf: String,
userId: String,
sfuSessionId: String,
allowed: Boolean
)

View File

@ -1 +1 @@
git clone --branch v2.9.0-alpha.0 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-sfu bbb-webrtc-sfu
git clone --branch v2.9.0-alpha.3 --depth 1 https://github.com/bigbluebutton/bbb-webrtc-sfu bbb-webrtc-sfu

View File

@ -421,7 +421,15 @@ start_bigbluebutton () {
TOMCAT_SERVICE=$TOMCAT_USER
fi
systemctl start $TOMCAT_SERVICE nginx freeswitch $REDIS_SERVICE bbb-apps-akka bbb-fsesl-akka bbb-rap-resque-worker bbb-rap-starter.service bbb-rap-caption-inbox.service $HTML5 $WEBHOOKS $ETHERPAD $PADS $BBB_WEB $BBB_LTI
systemctl start $TOMCAT_SERVICE || {
echo
echo "# Warning: $TOMCAT_SERVICE could not be started. Please, check BBB-LTI or BBB-Demo."
echo "# Run the command:"
echo "# sudo journalctl -u $TOMCAT_SERVICE"
echo "# To better understand the ERROR"
}
systemctl start nginx freeswitch $REDIS_SERVICE bbb-apps-akka bbb-fsesl-akka bbb-rap-resque-worker bbb-rap-starter.service bbb-rap-caption-inbox.service $HTML5 $WEBHOOKS $ETHERPAD $PADS $BBB_WEB $BBB_LTI
if [ -f /usr/lib/systemd/system/bbb-html5.service ]; then
systemctl start mongod
@ -1134,10 +1142,19 @@ check_state() {
fi
if bbb-conf --status | grep -q inactive; then
echo "# Error: Detected some processes have not started correctly"
echo "#"
echo "# $(bbb-conf --status | grep inactive)"
echo "#"
if bbb-conf --status | grep -q tomcat9; then
echo "# Warning: $TOMCAT_SERVICE is not started correctly"
echo "#"
echo "# $(bbb-conf --status | grep inactive | grep $TOMCAT_SERVICE)"
echo "#"
fi
if bbb-conf --status | grep inactive | grep -vq tomcat9; then
echo "# Error: Detected some processes have not started correctly"
echo "#"
echo "# $(bbb-conf --status | grep inactive | grep -v $TOMCAT_SERVICE)"
echo "#"
fi
fi
if systemctl status freeswitch | grep -q SETSCHEDULER; then

View File

@ -134,7 +134,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<script src="compatibility/tflite-simd.js?v=VERSION" language="javascript"></script>
<script src="compatibility/tflite.js?v=VERSION" language="javascript"></script>
<!-- fonts -->
<link rel="preload" href="fonts/BbbIcons/bbb-icons.woff?v=VERSION" as="font" crossorigin="anonymous"/>
<link rel="preload" href="fonts/BbbIcons/bbb-icons.woff2?v=VERSION" as="font" type="font/woff2" crossorigin="anonymous"/>
<link rel="preload" href="fonts/SourceSansPro/SourceSansPro-Light.woff?v=VERSION" as="font" crossorigin="anonymous"/>
<link rel="preload" href="fonts/SourceSansPro/SourceSansPro-Regular.woff?v=VERSION" as="font" crossorigin="anonymous"/>
<link rel="preload" href="fonts/SourceSansPro/SourceSansPro-Semibold.woff?v=VERSION" as="font" crossorigin="anonymous"/>
@ -147,7 +147,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
<style>
@font-face {
font-family: 'bbb-icons';
src: url('fonts/BbbIcons/bbb-icons.woff?v=VERSION') format('woff');
src: url('fonts/BbbIcons/bbb-icons.woff2?v=VERSION') format('woff2'),
url('fonts/BbbIcons/bbb-icons.woff?v=VERSION') format('woff');
font-weight: normal;
font-style: normal;
}

View File

@ -345,3 +345,6 @@
.icon-bbb-pin-video_off:before {
content: "\e965";
}
.icon-bbb-closed_caption_stop:before {
content: "\e966";
}

View File

@ -28,7 +28,6 @@ const MEDIA = Meteor.settings.public.media;
const DEFAULT_FULLAUDIO_MEDIA_SERVER = MEDIA.audio.fullAudioMediaServer;
const LISTEN_ONLY_OFFERING = MEDIA.listenOnlyOffering;
const MEDIA_TAG = MEDIA.mediaTag.replace(/#/g, '');
const GLOBAL_AUDIO_PREFIX = 'GLOBAL_AUDIO_';
const RECONNECT_TIMEOUT_MS = MEDIA.listenOnlyCallTimeout || 15000;
const SENDRECV_ROLE = 'sendrecv';
const RECV_ROLE = 'recv';
@ -304,14 +303,9 @@ export default class SFUAudioBridge extends BaseAudioBridge {
const { isListenOnly, extension, inputStream } = options;
this.inEchoTest = !!extension;
this.isListenOnly = isListenOnly;
const callerIdName = [
`${this.userId}_${getAudioSessionNumber()}`,
'bbbID',
isListenOnly ? `${GLOBAL_AUDIO_PREFIX}` : this.name,
].join('-').replace(/"/g, "'");
const brokerOptions = {
caleeName: callerIdName,
clientSessionNumber: getAudioSessionNumber(),
extension,
iceServers: this.iceServers,
mediaServer: getMediaServerAdapter(isListenOnly),
@ -431,8 +425,7 @@ export default class SFUAudioBridge extends BaseAudioBridge {
fetchWebRTCMappedStunTurnServers(this.sessionToken)
.then((iceServers) => {
const options = {
userName: this.name,
caleeName: `${GLOBAL_AUDIO_PREFIX}${this.voiceBridge}`,
clientSessionNumber: getAudioSessionNumber(),
iceServers,
offering: LISTEN_ONLY_OFFERING,
};
@ -471,8 +464,11 @@ export default class SFUAudioBridge extends BaseAudioBridge {
const mediaElement = document.getElementById(MEDIA_TAG);
this.clearReconnectionTimeout();
this.broker.stop();
this.broker = null;
if (this.broker) {
this.broker.stop();
this.broker = null;
}
if (mediaElement && typeof mediaElement.pause === 'function') {
mediaElement.pause();

View File

@ -296,7 +296,7 @@ class InputStreamLiveSelector extends Component {
size="lg"
circle
accessKey={shortcuts.togglemute}
talking={talking || undefined}
$talking={talking || undefined}
animations={animations}
data-test="toggleMicrophoneButton"
/>

View File

@ -40,15 +40,15 @@ const MuteToggleButton = styled(Button)`
}
`}
${({ talking }) => talking && `
${({ $talking }) => $talking && `
border-radius: 50%;
`}
${({ talking, animations }) => talking && animations && css`
${({ $talking, animations }) => $talking && animations && css`
animation: ${pulse} 1s infinite ease-in;
`}
${({ talking, animations }) => talking && !animations && css`
${({ $talking, animations }) => $talking && !animations && css`
& span {
content: '';
outline: none !important;

View File

@ -21,7 +21,7 @@ const Icon = ({
<i
className={cx(className, [prependIconName, iconName].join(''))}
// ToastContainer from react-toastify passes a useless closeToast prop here
{..._.omit(props, ['closeToast', 'animations'])}
{..._.omit(props, ['closeToast', 'animations', 'rotate'])}
/>
);

View File

@ -96,7 +96,7 @@ const Adapter = () => {
/* needed to prevent an issue with dupĺicated messages when user role is changed
more info: https://github.com/bigbluebutton/bigbluebutton/issues/11842 */
useEffect(() => {
if (users[Auth.meetingID]) {
if (users[Auth.meetingID] && users[Auth.meetingID][Auth.userID]) {
if (currentUserData?.role !== users[Auth.meetingID][Auth.userID].role) {
prevUserData = currentUserData;
}

View File

@ -242,6 +242,7 @@ class RecordingIndicator extends PureComponent {
aria-label={`${intl.formatMessage(recording
? intlMessages.notificationRecordingStart
: intlMessages.notificationRecordingStop)}`}
recording={recording}
>
{recordingIndicatorIcon}

View File

@ -126,6 +126,13 @@ const RecordingIndicator = styled.div`
const RecordingStatusViewOnly = styled.div`
display: flex;
${({ recording }) => recording && `
padding: 5px;
background-color: ${colorDanger};
border: ${borderSizeLarge} solid ${colorDanger};
border-radius: 10px;
`}
`;
export default {

View File

@ -66,9 +66,9 @@ class TalkingIndicator extends PureComponent {
return (
<Styled.TalkingIndicatorButton
spoke={!talking}
muted={muted}
isViewer={!amIModerator}
$spoke={!talking || undefined}
$muted={muted}
$isViewer={!amIModerator || undefined}
key={_.uniqueId(`${callerName}-`)}
onClick={() => this.handleMuteUser(id)}
label={callerName}
@ -109,9 +109,9 @@ class TalkingIndicator extends PureComponent {
return (
<Styled.TalkingIndicatorButton
spoke={nobodyTalking}
muted={false}
isViewer={false}
$spoke={nobodyTalking}
$muted={false}
$isViewer={false}
key={_.uniqueId('_has__More_')}
onClick={() => {}} // maybe add a dropdown to show the rest of the users
label="..."

View File

@ -45,7 +45,7 @@ const TalkingIndicatorButton = styled(Button)`
@media ${phoneLandscape} {
height: 1rem;
}
i,
span {
position: relative;
@ -92,7 +92,7 @@ const TalkingIndicatorButton = styled(Button)`
opacity: 1;
}
${({ spoke }) => spoke && `
${({ $spoke }) => $spoke && `
opacity: ${spokeOpacity};
[dir="rtl"] & {
@ -100,15 +100,15 @@ const TalkingIndicatorButton = styled(Button)`
}
`}
${({ muted }) => muted && `
${({ $muted }) => $muted && `
cursor: default;
i {
background-color: ${colorDanger};
}
`}
${({ isViewer }) => isViewer && `
${({ $isViewer }) => $isViewer && `
cursor: default;
`}
`;

View File

@ -201,7 +201,7 @@ const VideoListItem = (props) => {
fullscreen={isFullscreenContext}
data-test={talking ? 'webcamItemTalkingUser' : 'webcamItem'}
animations={animations}
{...makeDragOperations(onVirtualBgDrop, user.userId)}
{...makeDragOperations(onVirtualBgDrop, user?.userId)}
>
{
videoIsReady

View File

@ -58,7 +58,7 @@ const DragAndDrop = (props) => {
}, []);
const makeDragOperations = (onAction, userId) => {
if (Auth.userID !== userId || !ENABLE_WEBCAM_BACKGROUND_UPLOAD) return {};
if (!userId || Auth.userID !== userId || !ENABLE_WEBCAM_BACKGROUND_UPLOAD) return {};
const startAndSaveVirtualBackground = (file) => {
const { readFile } = VirtualBgService;

View File

@ -50,6 +50,7 @@ export default function Whiteboard(props) {
setIsZoomed,
zoomChanger,
isZoomed,
isMultiUserActive,
} = props;
const { pages, pageStates } = initDefaultPages(curPres?.pages.length || 1);
@ -221,8 +222,10 @@ export default function Whiteboard(props) {
<Cursors
tldrawAPI={tldrawAPI}
currentUser={currentUser}
hasMultiUserAccess={props?.hasMultiUserAccess}
whiteboardId={whiteboardId}
isViewersCursorLocked={isViewersCursorLocked}
isMultiUserActive={isMultiUserActive}
>
<Tldraw
key={`wb-${!hasWBAccess && !isPresenter}`}
@ -231,9 +234,32 @@ export default function Whiteboard(props) {
// until we handle saving of assets in akka.
disableAssets={true}
onMount={(app) => {
if (!hasWBAccess && !isPresenter) app.onPan = () => {};
setTLDrawAPI(app);
props.setTldrawAPI(app);
// disable for non presenter that doesn't have multi user access
if (!hasWBAccess && !isPresenter) {
app.onPan = () => {};
app.setSelectedIds = () => {};
app.setHoveredId = () => {};
} else {
// disable hover highlight for background slide shape
app.setHoveredId = (id) => {
if (id.includes('slide-background')) return null;
app.patchState(
{
document: {
pageStates: {
[app.getPage()?.id]: {
hoveredId: id,
},
},
},
},
`set_hovered_id`
);
};
}
if (curPageId) {
app.changePage(curPageId);
if (slidePosition.zoom === 0) {

View File

@ -1,5 +1,4 @@
import * as React from "react";
import ReactCursorPosition from "react-cursor-position";
import { _ } from "lodash";
function usePrevious(value) {
@ -17,7 +16,9 @@ const renderCursor = (
y,
currentPoint,
pageState,
owner = false
isMultiUserActive,
owner = false,
) => {
const z = !owner ? 2 : 1;
let _x = null;
@ -45,7 +46,7 @@ const renderCursor = (
}}
/>
<div
{isMultiUserActive && <div
style={{
zIndex: z,
position: "absolute",
@ -63,19 +64,20 @@ const renderCursor = (
}}
>
{name}
</div>
</div>}
</>
);
};
const PositionLabel = (props) => {
const {
position: { x = 0, y = 0 } = {},
currentUser,
currentPoint,
pageState,
publishCursorUpdate,
whiteboardId,
pos,
isMultiUserActive,
} = props;
const { name, color } = currentUser;
@ -83,9 +85,7 @@ const PositionLabel = (props) => {
React.useEffect(() => {
try {
const point = _.isEqual(currentPoint, prevCurrentPoint)
? [x, y]
: currentPoint;
const point = [pos.x, pos.y];
publishCursorUpdate({
xPercent:
point[0] / pageState?.camera?.zoom - pageState?.camera?.point[0],
@ -96,12 +96,12 @@ const PositionLabel = (props) => {
} catch (e) {
console.log(e);
}
}, [x, y]);
}, [pos?.x, pos?.y]);
return (
<>
<div style={{ position: "absolute", height: "100%", width: "100%" }}>
{renderCursor(name, color, x, y, currentPoint, props.pageState)}
{renderCursor(name, color, pos.x, pos.y, currentPoint, props.pageState, isMultiUserActive(whiteboardId))}
</div>
</>
);
@ -110,6 +110,7 @@ const PositionLabel = (props) => {
export default function Cursors(props) {
let cursorWrapper = React.useRef(null);
const [active, setActive] = React.useState(false);
const [pos, setPos] = React.useState({ x: 0, y: 0 });
const {
whiteboardId,
otherCursors,
@ -117,39 +118,80 @@ export default function Cursors(props) {
tldrawAPI,
publishCursorUpdate,
children,
isViewersCursorLocked
isViewersCursorLocked,
hasMultiUserAccess,
isMultiUserActive,
} = props;
const start = () => setActive(true);
const end = () => {
publishCursorUpdate({
xPercent: -1.0,
yPercent: -1.0,
whiteboardId: whiteboardId,
});
setActive(false);
};
const moved = (event) => {
const { type } = event;
const yOffset = parseFloat(document.getElementById('Navbar')?.style?.height);
const getSibling = (el) => el?.previousSibling || null;
const panel = getSibling(document.getElementById('Navbar'));
const subPanel = panel && getSibling(panel);
const xOffset = (parseFloat(panel?.style?.width) || 0) + (parseFloat(subPanel?.style?.width) || 0);
if (type === 'touchmove') {
!active && setActive(true);
return setPos({ x: event?.changedTouches[0]?.clientX - xOffset, y: event?.changedTouches[0]?.clientY - yOffset });
}
return setPos({ x: event.x - xOffset, y: event.y - yOffset });
}
React.useEffect(() => {
!cursorWrapper.hasOwnProperty("mouseenter") &&
cursorWrapper?.addEventListener("mouseenter", (event) => {
setActive(true);
});
cursorWrapper?.addEventListener("mouseenter", start);
!cursorWrapper.hasOwnProperty("mouseleave") &&
cursorWrapper?.addEventListener("mouseleave", (event) => {
publishCursorUpdate({
xPercent: -1.0,
yPercent: -1.0,
whiteboardId: whiteboardId,
});
setActive(false);
});
cursorWrapper?.addEventListener("mouseleave", end);
!cursorWrapper.hasOwnProperty("touchend") &&
cursorWrapper?.addEventListener("touchend", end);
!cursorWrapper.hasOwnProperty("mousemove") &&
cursorWrapper?.addEventListener("mousemove", moved);
!cursorWrapper.hasOwnProperty("touchmove") &&
cursorWrapper?.addEventListener("touchmove", moved);
}, [cursorWrapper]);
React.useEffect(() => {
return () => {
cursorWrapper.removeEventListener('mouseenter', start);
cursorWrapper.removeEventListener('mouseleave', end);
cursorWrapper.removeEventListener('mousemove', moved);
cursorWrapper.removeEventListener('touchend', end);
cursorWrapper.removeEventListener('touchmove', moved);
}
}, []);
return (
<span disabled={true} ref={(r) => (cursorWrapper = r)}>
<ReactCursorPosition style={{ height: "100%", cursor: "none" }}>
<div style={{ height: "100%", cursor: "none" }}>
{active && (
<PositionLabel
pos={pos}
otherCursors={otherCursors}
currentUser={currentUser}
currentPoint={tldrawAPI?.currentPoint}
pageState={tldrawAPI?.getPageState()}
publishCursorUpdate={publishCursorUpdate}
whiteboardId={whiteboardId}
isMultiUserActive={isMultiUserActive}
/>
)}
{children}
</ReactCursorPosition>
</div>
{otherCursors
.filter((c) => c?.xPercent && c?.yPercent)
.filter((c) => {
@ -159,19 +201,33 @@ export default function Cursors(props) {
return null;
})
.map((c) => {
return (
c &&
currentUser.userId !== c?.userId &&
renderCursor(
c?.userName,
c?.presenter ? "#C70039" : "#AFE1AF",
c?.xPercent,
c?.yPercent,
null,
tldrawAPI?.getPageState(),
true
)
);
if (c && currentUser.userId !== c?.userId) {
if (c.presenter) {
return renderCursor(
c?.userName,
"#C70039",
c?.xPercent,
c?.yPercent,
null,
tldrawAPI?.getPageState(),
isMultiUserActive(whiteboardId),
true
);
}
return hasMultiUserAccess(whiteboardId, c?.userId) && (
renderCursor(
c?.userName,
"#AFE1AF",
c?.xPercent,
c?.yPercent,
null,
tldrawAPI?.getPageState(),
isMultiUserActive(whiteboardId),
true
)
);
}
})}
</span>
);

View File

@ -15,6 +15,8 @@ export default
otherCursors: Service.getCurrentCursors(params.whiteboardId),
tldrawAPI: params.tldrawAPI,
isViewersCursorLocked: params.isViewersCursorLocked,
hasMultiUserAccess: params.hasMultiUserAccess,
isMultiUserActive: params.isMultiUserActive,
};
})(CursorsContainer)
;

View File

@ -18,7 +18,7 @@ class AudioBroker extends BaseBroker {
this.offering = true;
// Optional parameters are:
// caleeName,
// clientSessionNumber
// iceServers,
// offering,
// mediaServer,
@ -192,7 +192,7 @@ class AudioBroker extends BaseBroker {
id: 'start',
type: this.sfuComponent,
role: this.role,
caleeName: this.caleeName,
clientSessionNumber: this.clientSessionNumber,
sdpOffer: offer,
mediaServer: this.mediaServer,
extension: this.extension,

View File

@ -64,7 +64,6 @@
"react": "^17.0.2",
"react-autosize-textarea": "^5.0.1",
"react-color": "^2.19.3",
"react-cursor-position": "^3.0.3",
"react-dom": "^17.0.2",
"react-draggable": "^3.3.2",
"react-dropzone": "^7.0.1",

View File

@ -580,11 +580,11 @@ public:
# be used in Chromium browsers. Works around the fact that Chromium has no
# echo cancellation in non-rtc audio streams
localEchoTest:
enabled: false
initialHearingState: false
useRtcLoopbackInChromium: false
enabled: true
initialHearingState: true
useRtcLoopbackInChromium: true
# showVolumeMeter: shows an energy bar for microphones in the AudioSettings view
showVolumeMeter: false
showVolumeMeter: true
stats:
enabled: true
interval: 10000

View File

@ -42,6 +42,7 @@ class ConnectionController {
response.addHeader("User-Id", userSession.internalUserId)
response.addHeader("Meeting-Id", userSession.meetingID)
response.addHeader("Voice-Bridge", userSession.voicebridge )
response.addHeader("User-Name", userSession.fullname)
response.setStatus(200)
response.outputStream << 'authorized'
} else {

View File

@ -5,6 +5,7 @@ location /bbb-webrtc-sfu {
auth_request_set $user_id $sent_http_user_id;
auth_request_set $meeting_id $sent_http_meeting_id;
auth_request_set $voice_bridge $sent_http_voice_bridge;
auth_request_set $user_name $sent_http_user_name;
proxy_pass http://127.0.0.1:3008;
proxy_http_version 1.1;
@ -14,6 +15,8 @@ location /bbb-webrtc-sfu {
proxy_set_header User-Id $user_id;
proxy_set_header Meeting-Id $meeting_id;
proxy_set_header Voice-Bridge $voice_bridge;
proxy_set_header User-Name $user_name;
proxy_read_timeout 60s;
proxy_send_timeout 60s;
client_body_timeout 60s;

View File

@ -1,42 +0,0 @@
#!/bin/bash
set -ev
files=`git diff --name-only HEAD..$TRAVIS_BRANCH`
if [[ $files = *"bigbluebutton-html5"* ]]; then
cd bigbluebutton-html5
curl https://install.meteor.com/ | sh
meteor npm install
cd ..
if [ $1 = linter ]
then
html5_files=""
list=$(echo $files | tr " " "\n")
for file in $list
do
if [[ $file = bigbluebutton-html5* ]] && [[ -e $file ]]
then
html5_files+=" $file"
fi
done
bigbluebutton-html5/node_modules/.bin/eslint --ext .jsx,.js $html5_files
elif [ $1 = acceptance_tests ]
then
{
git clone --single-branch -b update-html5 https://github.com/bigbluebutton/docker.git
cp -r docker/{mod,restart.sh,setup.sh,supervisord.conf} .
cp -r docker/Dockerfile Dockerfile.test
docker build -t b2 -f Dockerfile.test .
docker=$(docker run -d -p 80:80/tcp -p 443:443/tcp -p 1935:1935 -p 5066:5066 -p 3478:3478 -p 3478:3478/udp b2 -h localhost)
echo $docker
} > /dev/null
cd tests/puppeteer/core
conf=$(docker exec $(docker ps -q) bbb-conf --secret | grep "Secret:")
secret=$(echo $conf | cut -d' ' -f2)
export BBB_SHARED_SECRET=$secret
node html5-check.js
cd ../../..
npm test
fi
fi

File diff suppressed because it is too large Load Diff

View File

@ -1 +0,0 @@
See [https://github.com/bigbluebutton/bigbluebutton-api-php](https://github.com/bigbluebutton/bigbluebutton-api-php).

View File

@ -1,53 +0,0 @@
.metadata
.project
.classpath
.settings
.history
.worksheet
gen
**/*.swp
**/*~.nib
**/build/
**/*.pbxuser
**/*.perspective
**/*.perspectivev3
*.xcworkspace
*.xcuserdatad
*.iml
project/*.ipr
project/*.iml
project/*.iws
project/out
project/*/target
project/target
project/*/bin
project/*/build
project/*.iml
project/*/*.iml
project/.idea
project/.idea/*
.idea/
.DS_Store
project/.DS_Store
project/*/.DS_Store
tm.out
tmlog*.log
*.tm*.epoch
out/
provisioning/.vagrant
provisioning/*/.vagrant
provisioning/*/*.known
/sbt/akka-patterns-store/
/daemon/src/build/
*.lock
logs/
tmp/
build/
akka-patterns-store/
lib_managed/
.cache
bin/
.vertx/
target/
src/main/resources/

View File

@ -1,31 +0,0 @@
#alignArguments=false
alignParameters=true
alignSingleLineCaseStatements=true
#alignSingleLineCaseStatements.maxArrowIndent=40
#allowParamGroupsOnNewlines=false
#compactControlReadability=false
#compactStringConcatenation=false
danglingCloseParenthesis=Force
#doubleIndentClassDeclaration=false
doubleIndentConstructorArguments=true
doubleIndentMethodDeclaration=true
firstArgumentOnNewline=Force
firstParameterOnNewline=Force
#formatXml=true
#indentLocalDefs=false
#indentPackageBlocks=true
#indentSpaces=2
#indentWithTabs=false
#multilineScaladocCommentsStartOnFirstLine=false
#newlineAtEndOfFile=false
#placeScaladocAsterisksBeneathSecondAsterisk=false
#preserveSpaceBeforeArguments=false
#rewriteArrowSymbols=false
singleCasePatternOnNewline=false
#spaceBeforeColon=false
#spaceBeforeContextColon=false
#spaceInsideBrackets=false
#spaceInsideParentheses=false
#spacesAroundMultiImports=true
#spacesWithinPatternBinders=true

View File

@ -1,75 +0,0 @@
import org.bigbluebutton.build._
import NativePackagerHelper._
import com.typesafe.sbt.SbtNativePackager.autoImport._
enablePlugins(JavaServerAppPackaging)
enablePlugins(UniversalPlugin)
enablePlugins(DebianPlugin)
val compileSettings = Seq(
organization := "org.bigbluebutton",
scalacOptions ++= List(
"-unchecked",
"-deprecation",
"-Xlint",
"-Ywarn-dead-code",
"-language:_",
"-target:jvm-1.8",
"-encoding", "UTF-8"
),
javacOptions ++= List(
"-Xlint:unchecked",
"-Xlint:deprecation"
)
)
publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath + "/dev/repo/maven-repo/releases")))
// We want to have our jar files in lib_managed dir.
// This way we'll have the right path when we import
// into eclipse.
retrieveManaged := true
testOptions in Test += Tests.Argument(TestFrameworks.Specs2, "html", "console", "junitxml")
testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/scalatest-reports")
Seq(Revolver.settings: _*)
lazy val bbbVertxAkka = (project in file(".")).settings(name := "bbb-vertx-akka", libraryDependencies ++= Dependencies.runtime).settings(compileSettings)
// See https://github.com/scala-ide/scalariform
// Config file is in ./.scalariform.conf
scalariformAutoformat := true
//-----------
// Packaging
//
// Reference:
// https://github.com/muuki88/sbt-native-packager-examples/tree/master/akka-server-app
// http://www.scala-sbt.org/sbt-native-packager/index.html
//-----------
mainClass := Some("org.bigbluebutton.Boot")
maintainer in Linux := "Richard Alam <ritzalam@gmail.com>"
packageSummary in Linux := "BigBlueButton Vertx Akka"
packageDescription := """BigBlueButton Core Vertx Akka."""
val user = "bigbluebutton"
val group = "bigbluebutton"
// user which will execute the application
daemonUser in Linux := user
// group which will execute the application
daemonGroup in Linux := group
javaOptions in Universal ++= Seq("-J-Xms130m", "-J-Xmx256m", "-Dconfig.file=conf/application.conf", "-Dlogback.configurationFile=conf/logback.xml")
debianPackageDependencies in Debian ++= Seq("java8-runtime-headless", "bash")

View File

@ -1,44 +0,0 @@
frontend ssl-in
mode tcp
log-format %ci:%cp\ [%t]\ %ft\ %b/%s\ %Tw/%Tc/%Tt\ %U:%B\ %ts\ %ac/%fc/%bc/%sc/%rc\ %hr\ %hs\ %sq/%bq
bind *:443,:::443 ssl crt /etc/ssl/ritz-ss.blindside-dev.com/ritz-ss.blindside-dev.com.pem
# Detect RTMP traffic
# The first byte must be 0x03 (version 3)
acl rtmp_handshake_ver req.payload(0,1) -m bin 03
# RTMP has a fixed-size handshake: 1 byte version + 1536 byte data.
# This acl causes haproxy to not detect a request as rtmp unless
# it's received at least that much data (and didn't match other things)
#acl rtmp_handshake_size req.len ge 1537
acl rtmp_handshake_size req.len ge 1
acl is_websocket path_beg -i /eventbus
#acl is_websocket hdr(Upgrade) -i WebSocket
# haproxy has built-in HTTP detection
# If we haven't received enough data to identify the protocol after
# 30 seconds, drop the connection
tcp-request inspect-delay 30s
tcp-request content accept if rtmp_handshake_ver rtmp_handshake_size
tcp-request content accept if HTTP
use_backend vertx if is_websocket
use_backend red5 if rtmp_handshake_ver rtmp_handshake_size
use_backend nginx if HTTP
backend nginx
mode http
option forwardfor
reqadd X-Forwarded-Proto:\ https
server nginx 127.0.0.1:80
backend red5
mode tcp
server red5 127.0.0.1:1935
backend vertx
server vertx 127.0.0.1:3001

View File

@ -1,13 +0,0 @@
server {
listen 80;
server_name 192.168.23.33;
access_log /var/log/nginx/vertx-akka.access.log;
# Vertx-Akka landing page.
location / {
root /var/www/vertx-akka;
index index.html index.htm;
expires 1m;
}
}

View File

@ -1,71 +0,0 @@
<!--
#%L
distributed-chat-service
%%
Copyright (C) 2015 Zanclus Consulting
%%
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
#L%
-->
<html>
<head>
<title>Distributed Chat Service</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
<script src="//cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js"></script>
<script src="vertxbus.js"></script>
<style>
.inset {
box-shadow: inset 0 0 4px #000000;
-moz-box-shadow: inset 0 0 4px #000000;
-webkit-box-shadow: inset 0 0 4px #000000;
width: 400px;
border-width: 4px;
padding: 5px;
}
input.inset {
height: 40px;
}
div.inset {
height: 500px;
white-space: pre-wrap
}
</style>
</head>
<body>
<script>
var eb = new vertx.EventBus("http://192.168.23.33:3001/eventbus");
eb.onopen = function () {
eb.registerHandler("chat.to.client", function (msg) {
$('#chat').append(msg + "\n");
});
};
function send(event) {
if (event.keyCode == 13 || event.which == 13) {
var message = $('#input').val();
if (message.length > 0) {
console.log($('#input'));
eb.publish("chat.to.server", message);
$('#input').val("");
}
}
}
</script>
<div id="chat" class="inset"></div>
<input id="input" type="text" onkeydown="send(event)" class="inset">
</body>
</html>

View File

@ -1,35 +0,0 @@
<html>
<head>
<title></title>
<script src="https://code.jquery.com/jquery-1.11.2.min.js"></script>
<script src="//cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js"></script>
<script src="vertxbus.js"></script>
</head>
<style>
.news {
font-size: 20pt;
}
</style>
<body>
<div class="news">Latest news: </div><br>
<div id="status" class="news"></div>
<script>
var eb = new vertx.EventBus("http://192.168.23.33:3000/eventbus");
eb.onopen = function() {
eb.registerHandler("news-feed", function(msg) {
var str = "<code>" + msg + "</code><br>";
$('#status').prepend(str);
})
}
</script>
</body>
</html>

View File

@ -1,228 +0,0 @@
/*
* Copyright 2014 Red Hat, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
/*
* Copyright (c) 2011-2013 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
var vertx = vertx || {};
!function(factory) {
if (typeof define === "function" && define.amd) {
// Expose as an AMD module with SockJS dependency.
// "vertxbus" and "sockjs" names are used because
// AMD module names are derived from file names.
define("vertxbus", ["sockjs"], factory);
} else {
// No AMD-compliant loader
factory(SockJS);
}
}(function(SockJS) {
vertx.EventBus = function(url, options) {
var that = this;
var sockJSConn = new SockJS(url, undefined, options);
var handlerMap = {};
var replyHandlers = {};
var state = vertx.EventBus.CONNECTING;
var pingTimerID = null;
var pingInterval = null;
if (options) {
pingInterval = options['vertxbus_ping_interval'];
}
if (!pingInterval) {
pingInterval = 5000;
}
that.onopen = null;
that.onclose = null;
that.send = function(address, message, replyHandler) {
sendOrPub("send", address, message, replyHandler)
}
that.publish = function(address, message) {
sendOrPub("publish", address, message, null)
}
that.registerHandler = function(address, handler) {
checkSpecified("address", 'string', address);
checkSpecified("handler", 'function', handler);
checkOpen();
var handlers = handlerMap[address];
if (!handlers) {
handlers = [handler];
handlerMap[address] = handlers;
// First handler for this address so we should register the connection
var msg = { type : "register",
address: address };
sockJSConn.send(JSON.stringify(msg));
} else {
handlers[handlers.length] = handler;
}
}
that.unregisterHandler = function(address, handler) {
checkSpecified("address", 'string', address);
checkSpecified("handler", 'function', handler);
checkOpen();
var handlers = handlerMap[address];
if (handlers) {
var idx = handlers.indexOf(handler);
if (idx != -1) handlers.splice(idx, 1);
if (handlers.length == 0) {
// No more local handlers so we should unregister the connection
var msg = { type : "unregister",
address: address};
sockJSConn.send(JSON.stringify(msg));
delete handlerMap[address];
}
}
}
that.close = function() {
checkOpen();
state = vertx.EventBus.CLOSING;
sockJSConn.close();
}
that.readyState = function() {
return state;
}
sockJSConn.onopen = function() {
// Send the first ping then send a ping every pingInterval milliseconds
sendPing();
pingTimerID = setInterval(sendPing, pingInterval);
state = vertx.EventBus.OPEN;
if (that.onopen) {
that.onopen();
}
};
sockJSConn.onclose = function() {
state = vertx.EventBus.CLOSED;
if (pingTimerID) clearInterval(pingTimerID);
if (that.onclose) {
that.onclose();
}
};
sockJSConn.onmessage = function(e) {
var msg = e.data;
var json = JSON.parse(msg);
var type = json.type;
if (type === 'err') {
console.error("Error received on connection: " + json.body);
return;
}
var body = json.body;
var replyAddress = json.replyAddress;
var address = json.address;
var replyHandler;
if (replyAddress) {
replyHandler = function(reply, replyHandler) {
// Send back reply
that.send(replyAddress, reply, replyHandler);
};
}
var handlers = handlerMap[address];
if (handlers) {
// We make a copy since the handler might get unregistered from within the
// handler itself, which would screw up our iteration
var copy = handlers.slice(0);
for (var i = 0; i < copy.length; i++) {
copy[i](body, replyHandler);
}
} else {
// Might be a reply message
var handler = replyHandlers[address];
if (handler) {
delete replyHandlers[address];
handler(body, replyHandler);
}
}
}
function sendPing() {
var msg = {
type: "ping"
}
sockJSConn.send(JSON.stringify(msg));
}
function sendOrPub(sendOrPub, address, message, replyHandler) {
checkSpecified("address", 'string', address);
checkSpecified("replyHandler", 'function', replyHandler, true);
checkOpen();
var envelope = { type : sendOrPub,
address: address,
body: message };
if (replyHandler) {
var replyAddress = makeUUID();
envelope.replyAddress = replyAddress;
replyHandlers[replyAddress] = replyHandler;
}
var str = JSON.stringify(envelope);
sockJSConn.send(str);
}
function checkOpen() {
if (state != vertx.EventBus.OPEN) {
throw new Error('INVALID_STATE_ERR');
}
}
function checkSpecified(paramName, paramType, param, optional) {
if (!optional && !param) {
throw new Error("Parameter " + paramName + " must be specified");
}
if (param && typeof param != paramType) {
throw new Error("Parameter " + paramName + " must be of type " + paramType);
}
}
function isFunction(obj) {
return !!(obj && obj.constructor && obj.call && obj.apply);
}
function makeUUID(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx"
.replace(/[xy]/g,function(a,b){return b=Math.random()*16,(a=="y"?b&3|8:b|0).toString(16)})}
}
vertx.EventBus.CONNECTING = 0;
vertx.EventBus.OPEN = 1;
vertx.EventBus.CLOSING = 2;
vertx.EventBus.CLOSED = 3;
return vertx.EventBus;
});

View File

@ -1,99 +0,0 @@
package org.bigbluebutton.build
import sbt._
import Keys._
object Dependencies {
object Versions {
// Scala
val scala = "2.12.8"
val junit = "4.12"
val junitInterface = "0.11"
val scalactic = "3.0.3"
// Libraries
val akkaVersion = "2.5.19"
val gson = "2.8.5"
val jackson = "2.9.7"
val logback = "1.2.3"
val quicklens = "1.4.11"
val spray = "1.3.4"
val vertxV = "3.5.1"
// Apache Commons
val lang = "3.8.1"
val codec = "1.11"
// BigBlueButton
val bbbCommons = "0.0.20-SNAPSHOT"
// Test
val scalaTest = "3.0.5"
val mockito = "2.23.0"
val akkaTestKit = "2.5.18"
}
object Compile {
val scalaLibrary = "org.scala-lang" % "scala-library" % Versions.scala
val scalaCompiler = "org.scala-lang" % "scala-compiler" % Versions.scala
val akkaActor = "com.typesafe.akka" % "akka-actor_2.12" % Versions.akkaVersion
val akkaSl4fj = "com.typesafe.akka" % "akka-slf4j_2.12" % Versions.akkaVersion
val vertxWeb = "io.vertx" % "vertx-web" % Versions.vertxV
val vertxAuthCommon = "io.vertx" % "vertx-auth-common" % Versions.vertxV
val vertxAuthShiro = "io.vertx" % "vertx-auth-shiro" % Versions.vertxV
val vertxWebScala = "io.vertx" %% "vertx-web-scala" % Versions.vertxV
val vertxLangScala = "io.vertx" %% "vertx-lang-scala" % Versions.vertxV
val googleGson = "com.google.code.gson" % "gson" % Versions.gson
val jacksonModule = "com.fasterxml.jackson.module" %% "jackson-module-scala" % Versions.jackson
val quicklens = "com.softwaremill.quicklens" %% "quicklens" % Versions.quicklens
val logback = "ch.qos.logback" % "logback-classic" % Versions.logback
val commonsCodec = "commons-codec" % "commons-codec" % Versions.codec
val sprayJson = "io.spray" % "spray-json_2.12" % Versions.spray
val redisEtaty = "com.github.etaty" % "rediscala_2.12" % "1.8.0"
val apacheLang = "org.apache.commons" % "commons-lang3" % Versions.lang
val bbbCommons = "org.bigbluebutton" % "bbb-common-message_2.12" % Versions.bbbCommons excludeAll (
ExclusionRule(organization = "org.red5"))
}
object Test {
val scalaTest = "org.scalatest" %% "scalatest" % Versions.scalaTest % "test"
val junit = "junit" % "junit" % Versions.junit % "test"
val mockitoCore = "org.mockito" % "mockito-core" % Versions.mockito % "test"
val scalactic = "org.scalactic" % "scalactic_2.12" % Versions.scalactic % "test"
val akkaTestKit = "com.typesafe.akka" %% "akka-testkit" % Versions.akkaTestKit % "test"
}
val testing = Seq(
Test.scalaTest,
Test.junit,
Test.mockitoCore,
Test.scalactic,
Test.akkaTestKit)
val runtime = Seq(
Compile.scalaLibrary,
Compile.scalaCompiler,
Compile.akkaActor,
Compile.akkaSl4fj,
Compile.vertxWeb,
Compile.vertxAuthCommon,
Compile.vertxAuthShiro,
Compile.vertxWebScala,
Compile.vertxWebScala,
Compile.googleGson,
Compile.jacksonModule,
Compile.quicklens,
Compile.logback,
Compile.commonsCodec,
Compile.sprayJson,
Compile.apacheLang,
Compile.redisEtaty,
Compile.bbbCommons) ++ testing
}

View File

@ -1 +0,0 @@
sbt.version=1.2.7

View File

@ -1 +0,0 @@

View File

@ -1,11 +0,0 @@
addSbtPlugin("io.spray" % "sbt-revolver" % "0.9.1")
addSbtPlugin("com.typesafe.sbteclipse" % "sbteclipse-plugin" % "5.2.4")
addSbtPlugin("org.scalariform" % "sbt-scalariform" % "1.8.2")
addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.3.15")
addSbtPlugin("net.vonbuchholtz" % "sbt-dependency-check" % "0.2.9")
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0")

View File

@ -1,6 +0,0 @@
#!/usr/bin/env bash
rm -rf src/main/resources
cp -R src/universal/conf src/main/resources
sbt run

View File

@ -1,6 +0,0 @@
#!/usr/bin/env bash
sbt clean stage
sudo service bbb-vertx-akka stop
cd target/universal/stage
./bin/bbb-vertx-akka

Binary file not shown.

View File

@ -1,12 +0,0 @@
package org.bigbluebutton.client;
import org.bigbluebutton.client.bus.ConnInfo2;
public interface IClientInGW {
void connect(ConnInfo2 connInfo);
void disconnect(ConnInfo2 connInfo);
void handleMsgFromClient(ConnInfo2 connInfo, String json);
void send(String channel, String json);
}

View File

@ -1,46 +0,0 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.red5.client.messaging;
import java.util.Map;
public class BroadcastClientMessage implements ClientMessage {
private String meetingID;
private Map<String, Object> message;
private String messageName;
public BroadcastClientMessage(String meetingID, String messageName, Map<String, Object> message) {
this.meetingID = meetingID;
this.message = message;
this.messageName = messageName;
}
public String getMeetingID() {
return meetingID;
}
public String getMessageName() {
return messageName;
}
public Map<String, Object> getMessage() {
return message;
}
}

View File

@ -1,19 +0,0 @@
package org.bigbluebutton.red5.client.messaging;
public class BroadcastToMeetingMsg implements ClientMessage {
public final String meetingId;
public final String messageName;
public final String json;
public BroadcastToMeetingMsg(String meetingId, String messageName, String json) {
this.meetingId = meetingId;
this.messageName = messageName;
this.json = json;
}
public String getMessageName() {
return messageName;
}
}

View File

@ -1,25 +0,0 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.red5.client.messaging;
public interface ClientMessage {
String getMessageName();
}

View File

@ -1,17 +0,0 @@
package org.bigbluebutton.red5.client.messaging;
public class CloseConnectionMsg implements ClientMessage {
public final String meetingId;
public final String connId;
public CloseConnectionMsg(String meetingId, String connId) {
this.meetingId = meetingId;
this.connId = connId;
}
public String getMessageName() {
return "CloseConnectionMsg";
}
}

View File

@ -1,15 +0,0 @@
package org.bigbluebutton.red5.client.messaging;
public class CloseMeetingAllConnectionsMsg implements ClientMessage {
public final String meetingId;
public CloseMeetingAllConnectionsMsg(String meetingId) {
this.meetingId = meetingId;
}
public String getMessageName() {
return "CloseMeetingAllConnectionsMsg";
}
}

View File

@ -1,61 +0,0 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.red5.client.messaging;
import java.util.Map;
public class DirectClientMessage implements ClientMessage {
private String meetingID;
private String userID;
private Map<String, Object> message;
private String messageName;
private String sharedObjectName;
public DirectClientMessage(String meetingID, String userID, String messageName, Map<String, Object> message) {
this.meetingID = meetingID;
this.userID = userID;
this.message = message;
this.messageName = messageName;
}
public void setSharedObjectName(String name) {
sharedObjectName = name;
}
public String getSharedObjectName() {
return sharedObjectName;
}
public String getMeetingID() {
return meetingID;
}
public String getUserID() {
return userID;
}
public String getMessageName() {
return messageName;
}
public Map<String, Object> getMessage() {
return message;
}
}

View File

@ -1,21 +0,0 @@
package org.bigbluebutton.red5.client.messaging;
public class DirectToClientMsg implements ClientMessage{
public final String meetingId;
public final String connId;
public final String json;
public final String messageName;
public DirectToClientMsg(String meetingId, String connId, String messageName, String json) {
this.meetingId = meetingId;
this.connId = connId;
this.messageName = messageName;
this.json = json;
}
public String getMessageName() {
return messageName;
}
}

View File

@ -1,18 +0,0 @@
package org.bigbluebutton.red5.client.messaging;
public class DisconnectAllClientsMessage implements ClientMessage {
private final String meetingId;
public DisconnectAllClientsMessage(String meetingId) {
this.meetingId = meetingId;
}
public String getMeetingId() {
return meetingId;
}
public String getMessageName() {
return "DisconnectAllClientsMessage";
}
}

View File

@ -1,8 +0,0 @@
package org.bigbluebutton.red5.client.messaging;
public class DisconnectAllMessage implements ClientMessage {
public String getMessageName() {
return "DisconnectAllMessage";
}
}

View File

@ -1,24 +0,0 @@
package org.bigbluebutton.red5.client.messaging;
public class DisconnectClientMessage implements ClientMessage {
private final String meetingId;
private final String userId;
public DisconnectClientMessage(String meetingId, String userId) {
this.meetingId = meetingId;
this.userId = userId;
}
public String getMeetingId() {
return meetingId;
}
public String getUserId() {
return userId;
}
public String getMessageName() {
return "DisconnectClientMessage";
}
}

View File

@ -1,6 +0,0 @@
package org.bigbluebutton.red5.client.messaging;
public interface IConnectionInvokerService {
void sendMessage(final ClientMessage message);
}

View File

@ -1,59 +0,0 @@
/**
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
*
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation; either version 3.0 of the License, or (at your option) any later
* version.
*
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
*
*/
package org.bigbluebutton.red5.client.messaging;
import java.util.ArrayList;
public class SharedObjectClientMessage implements ClientMessage {
public static final String BROADCAST = "broadcast";
public static final String DIRECT = "direct";
public static final String SHAREDOBJECT = "sharedobject";
private String meetingID;
private String sharedObjectName;
private ArrayList<Object> message;
private String messageName;
public SharedObjectClientMessage(String meetingID, String sharedObjectName, String messageName, ArrayList<Object> message) {
this.meetingID = meetingID;
this.message = message;
this.sharedObjectName = sharedObjectName;
this.messageName = messageName;
}
public void setSharedObjectName(String name) {
sharedObjectName = name;
}
public String getSharedObjectName() {
return sharedObjectName;
}
public String getMeetingID() {
return meetingID;
}
public String getMessageName() {
return messageName;
}
public ArrayList<Object> getMessage() {
return message;
}
}

View File

@ -1,19 +0,0 @@
package org.bigbluebutton.vertx;
import io.vertx.core.Vertx;
public class AkkaToVertxGateway implements IAkkaToVertxGateway{
private final Vertx vertx;
public AkkaToVertxGateway(Vertx vertx) {
this.vertx = vertx;
}
@Override
public void send(String json) {
vertx.eventBus().publish("to-vertx", json);
}
}

View File

@ -1,54 +0,0 @@
package org.bigbluebutton.vertx;
import io.vertx.core.AbstractVerticle;
import io.vertx.ext.auth.AuthProvider;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.*;
import io.vertx.ext.web.sstore.LocalSessionStore;
import org.bigbluebutton.VertxToAkkaGateway;
public class AuthenticateVerticle extends AbstractVerticle {
private final VertxToAkkaGateway gw;
public AuthenticateVerticle(VertxToAkkaGateway gw) {
this.gw = gw;
}
@Override
public void start() throws Exception {
Router router = Router.router(vertx);
// We need cookies, sessions and request bodies
router.route().handler(CookieHandler.create());
router.route().handler(BodyHandler.create());
router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));
// Simple auth service which uses a properties file for user/role info
AuthProvider authProvider = new MyAuthProvider(vertx);
// We need a user session handler too to make sure the user is stored in the session between requests
router.route().handler(UserSessionHandler.create(authProvider));
// Any requests to URI starting '/private/' require login
router.route("/private/*").handler(RedirectAuthHandler.create(authProvider, "/loginpage.html"));
// Serve the static private pages from directory 'private'
router.route("/private/*").handler(StaticHandler.create().setCachingEnabled(false).setWebRoot("private"));
// Handles the actual login
router.route("/loginhandler").handler(FormLoginHandler.create(authProvider));
// Implement logout
router.route("/logout").handler(context -> {
context.clearUser();
// Redirect back to the index page
context.response().putHeader("location", "/").setStatusCode(302).end();
});
// Serve the non private static pages
router.route().handler(StaticHandler.create());
vertx.createHttpServer().requestHandler(router::accept).listen(4000);
}
}

View File

@ -1,91 +0,0 @@
package org.bigbluebutton.vertx;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.MultiMap;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="http://tfox.org">Tim Fox</a>
*/
public class BbbApi extends AbstractVerticle {
private Map<String, JsonObject> products = new HashMap<>();
@Override
public void start() {
setUpInitialData();
Router router = Router.router(vertx);
router.route().handler(BodyHandler.create());
router.get("/products/:productID").handler(this::handleGetProduct);
router.put("/products/:productID").handler(this::handleAddProduct);
router.get("/products").handler(this::handleListProducts);
router.get("/bigbluebutton/api/create").handler(this::handleListProducts);
vertx.createHttpServer().requestHandler(router::accept).listen(4000);
}
private void handleGetProduct(RoutingContext routingContext) {
String productID = routingContext.request().getParam("productID");
HttpServerResponse response = routingContext.response();
if (productID == null) {
sendError(400, response);
} else {
JsonObject product = products.get(productID);
if (product == null) {
sendError(404, response);
} else {
response.putHeader("content-type", "application/json").end(product.encodePrettily());
}
}
}
private void handleAddProduct(RoutingContext routingContext) {
String productID = routingContext.request().getParam("productID");
HttpServerResponse response = routingContext.response();
if (productID == null) {
sendError(400, response);
} else {
JsonObject product = routingContext.getBodyAsJson();
if (product == null) {
sendError(400, response);
} else {
products.put(productID, product);
response.end();
}
}
}
private void handleListProducts(RoutingContext routingContext) {
MultiMap params = routingContext.request().params();
System.out.println("Name: " + params.get("name"));
JsonArray arr = new JsonArray();
products.forEach((k, v) -> arr.add(v));
routingContext.response().putHeader("content-type", "application/json").end(arr.encodePrettily());
}
private void sendError(int statusCode, HttpServerResponse response) {
response.setStatusCode(statusCode).end();
}
private void setUpInitialData() {
addProduct(new JsonObject().put("id", "prod3568").put("name", "Egg Whisk").put("price", 3.99).put("weight", 150));
addProduct(new JsonObject().put("id", "prod7340").put("name", "Tea Cosy").put("price", 5.99).put("weight", 100));
addProduct(new JsonObject().put("id", "prod8643").put("name", "Spatula").put("price", 1.00).put("weight", 80));
}
private void addProduct(JsonObject product) {
products.put(product.getString("id"), product);
}
}

View File

@ -1,50 +0,0 @@
package org.bigbluebutton.vertx;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.json.JsonObject;
import io.vertx.core.json.JsonArray;
import io.vertx.ext.auth.AbstractUser;
import io.vertx.ext.auth.AuthProvider;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.core.Future;
public class BbbUser extends AbstractUser {
private static final Logger log = LoggerFactory.getLogger(BbbUser.class);
private JsonObject jwtToken;
private JsonArray permissions;
public BbbUser(JsonObject jwtToken, JsonArray permissions) {
this.jwtToken = jwtToken;
this.permissions = permissions;
}
@Override
public JsonObject principal() {
return jwtToken;
}
@Override
public void setAuthProvider(AuthProvider arg0) {
// NOOP - JWT tokens are self contained :)
}
@Override
protected void doIsPermitted(String permission, Handler<AsyncResult<Boolean>> handler) {
if (permissions != null) {
for (Object jwtPermission : permissions) {
if (permission.equals(jwtPermission)) {
handler.handle(Future.succeededFuture(true));
return;
}
}
}
log.debug("User has no permission [" + permission + "]");
handler.handle(Future.succeededFuture(false));
}
}

View File

@ -1,75 +0,0 @@
package org.bigbluebutton.vertx;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.eventbus.EventBus;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.StaticHandler;
import io.vertx.ext.bridge.BridgeEventType;
import io.vertx.ext.web.handler.sockjs.BridgeOptions;
import io.vertx.ext.web.handler.sockjs.PermittedOptions;
import io.vertx.ext.web.handler.sockjs.SockJSHandler;
import io.vertx.ext.web.handler.sockjs.SockJSHandlerOptions;
import java.text.DateFormat;
import java.time.Instant;
import java.util.Date;
import org.bigbluebutton.VertxToAkkaGateway;
public class ChatVerticle extends AbstractVerticle {
private final VertxToAkkaGateway gw;
public ChatVerticle(VertxToAkkaGateway gw) {
this.gw = gw;
}
@Override
public void start() throws Exception {
Router router = Router.router(vertx);
// Allow events for the designated addresses in/out of the event bus bridge
BridgeOptions opts = new BridgeOptions()
.addInboundPermitted(new PermittedOptions().setAddress("chat.to.server"))
.addOutboundPermitted(new PermittedOptions().setAddress("chat.to.client"));
SockJSHandlerOptions options = new SockJSHandlerOptions().setHeartbeatInterval(2000);
// Create the event bus bridge and add it to the router.
SockJSHandler ebHandler = SockJSHandler.create(vertx, options);
router.route("/eventbus/*").handler(ebHandler);
// Create a router endpoint for the static content.
router.route().handler(StaticHandler.create());
ebHandler.bridge(opts, be -> {
if (be.type() == BridgeEventType.PUBLISH || be.type() == BridgeEventType.RECEIVE) {
if (be.getRawMessage().getString("body").equals("armadillos")) {
// Reject it
be.complete(false);
return;
}
}
be.complete(true);
});
// Start the web server and tell it to use the router to handle requests.
vertx.createHttpServer().requestHandler(router::accept).listen(3001);
EventBus eb = vertx.eventBus();
// Register to listen for messages coming IN to the server
eb.consumer("chat.to.server").handler(message -> {
// Create a timestamp string
String timestamp = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM).format(Date.from(Instant.now()));
// Send the message back out to all clients with the timestamp prepended.
gw.send(timestamp + ": " + message.body());
});
eb.consumer("to-vertx").handler(message -> {
eb.publish("chat.to.client", message.body());
});
}
}

View File

@ -1,35 +0,0 @@
package org.bigbluebutton.vertx;
import org.bigbluebutton.ConnectionManager;
import org.bigbluebutton.VertxToAkkaGateway;
import io.vertx.core.Vertx;
public class HelloWorld {
private final Vertx vertx;
private final VertxToAkkaGateway gw;
private final ConnectionManager cm;
public HelloWorld(Vertx vertx, VertxToAkkaGateway gw, ConnectionManager cm) {
this.vertx = vertx;
this.gw = gw;
this.cm = cm;
}
public void startup() {
// Create an HTTP server which simply returns "Hello World!" to each request.
//Vertx.vertx().createHttpServer().requestHandler(req -> req.response().end("Hello World! from gradle.")).listen(3000);
//vertx.deployVerticle(new ChatVerticle(gw));
//vertx.deployVerticle(new RealtimeVerticle());
//vertx.deployVerticle(new AuthenticateVerticle());
//vertx.deployVerticle(new PrivateVerticle(gw));
vertx.deployVerticle(new SockJSHandlerVerticle(cm));
//vertx.deployVerticle(new SimpleREST());
//vertx.deployVerticle(new BbbApi());
}
}

View File

@ -1,6 +0,0 @@
package org.bigbluebutton.vertx;
public interface IAkkaToVertxGateway {
void send(String json);
}

View File

@ -1,6 +0,0 @@
package org.bigbluebutton.vertx;
public interface IVertxToAkkaGateway {
void send(String json);
}

View File

@ -1,45 +0,0 @@
package org.bigbluebutton.vertx;
import org.bigbluebutton.VertxToAkkaGateway;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.eventbus.DeliveryOptions;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.AuthProvider;
import io.vertx.ext.auth.User;
import io.vertx.core.Future;
public class MyAuthProvider implements AuthProvider {
private final Vertx vertx;
public MyAuthProvider(Vertx vertx) {
this.vertx = vertx;
}
@Override
public void authenticate(JsonObject user, Handler<AsyncResult<User>> resultHandler) {
JsonObject object = new JsonObject();
object.put("foo", "bar").put("num", 123).put("mybool", true);
JsonArray array = new JsonArray();
array.add("foo").add(123).add(false);
DeliveryOptions options = new DeliveryOptions();
options.setSendTimeout(5000);
vertx.eventBus().send("to-akka-gw",
"Yay! Someone kicked a ball across a patch of grass",
options, ar -> {
if (ar.succeeded()) {
System.out.println("Received reply: " + ar.result().body());
System.out.println("Got Authenticated");
resultHandler.handle(Future.succeededFuture(new BbbUser(object, array)));
}
});
}
}

View File

@ -1,169 +0,0 @@
package org.bigbluebutton.vertx;
import java.text.DateFormat;
import java.time.Instant;
import java.util.Date;
import org.bigbluebutton.VertxToAkkaGateway;
import io.vertx.core.json.JsonObject;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.http.HttpServerOptions;
import io.vertx.core.net.JksOptions;
import io.vertx.ext.auth.AuthProvider;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.Session;
import io.vertx.ext.web.handler.*;
import io.vertx.ext.bridge.BridgeEventType;
import io.vertx.ext.web.handler.sockjs.BridgeOptions;
import io.vertx.ext.web.handler.sockjs.PermittedOptions;
import io.vertx.ext.web.handler.sockjs.SockJSHandler;
import io.vertx.ext.web.handler.sockjs.SockJSHandlerOptions;
import io.vertx.ext.web.sstore.LocalSessionStore;
public class PrivateVerticle extends AbstractVerticle {
private final VertxToAkkaGateway gw;
public PrivateVerticle(VertxToAkkaGateway gw) {
this.gw = gw;
}
@Override
public void start() throws Exception {
Router router = Router.router(vertx);
// We need cookies, sessions and request bodies
router.route().handler(CookieHandler.create());
router.route().handler(BodyHandler.create());
router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));
// Simple auth service which uses a properties file for user/role info
//AuthProvider authProvider = new MyAuthProvider(vertx);
// We need a user session handler too to make sure the user is stored in the session between requests
//router.route().handler(UserSessionHandler.create(authProvider));
// Handles the actual login
//router.route("/loginhandler").handler(FormLoginHandler.create(authProvider));
router.route("/private/*").handler(routingContext -> {
// This will require a login
// This will have the value true
boolean isAuthenticated = routingContext.user() != null;
if (isAuthenticated) {
System.out.println("**** User is authenticated.");
} else {
System.out.println("**** User is NOT authenticated.");
}
Session session = routingContext.session();
Integer cnt = session.get("hitcount");
cnt = (cnt == null ? 0 : cnt) + 1;
session.put("hitcount", cnt);
// routingContext.response().putHeader("content-type", "text/html")
// .end("<html><body><h1>Hitcount: " + cnt + "</h1></body></html>");
routingContext.next();
});
// Any requests to URI starting '/private/' require login
//router.route("/private/*").handler(RedirectAuthHandler.create(authProvider, "/loginpage.html"));
// Serve the static private pages from directory 'private'
//router.route("/private/*").handler(StaticHandler.create().setCachingEnabled(false).setWebRoot("private"));
// Implement logout
router.route("/logout").handler(context -> {
context.clearUser();
// Redirect back to the index page
context.response().putHeader("location", "/").setStatusCode(302).end();
});
// Allow events for the designated addresses in/out of the event bus bridge
BridgeOptions opts = new BridgeOptions()
.addInboundPermitted(new PermittedOptions().setAddress("chat.to.server"))
.addOutboundPermitted(new PermittedOptions().setAddress("chat.to.client"));
SockJSHandlerOptions options = new SockJSHandlerOptions().setHeartbeatInterval(2000);
// Create the event bus bridge and add it to the router.
SockJSHandler sockJSHandler = SockJSHandler.create(vertx, options);
router.route("/eventbus/*").handler(sockJSHandler);
// SockJSHandlerFactory sockJsMessageHandler = new SockJSHandlerFactory();
// sockJsMessageHandler.setupHandler(ebHandler, opts);
EventBus eb = vertx.eventBus();
sockJSHandler.bridge(opts, be -> {
if (be.type() == BridgeEventType.SOCKET_CREATED) {
System.out.println("Socket create for session: " + be.socket().webSession().id() + " socketWriteId:" + be.socket().writeHandlerID());
} else if (be.type() == BridgeEventType.SOCKET_CLOSED) {
System.out.println("Socket closed for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
// } else if (be.type() == BridgeEventType.SOCKET_IDLE) {
// System.out.println("Socket SOCKET_IDLE for: " + be.socket().webSession().id());
// } else if (be.type() == BridgeEventType.SOCKET_PING) {
// System.out.println("Socket SOCKET_PING for: " + be.socket().webSession().id());
} else if (be.type() == BridgeEventType.UNREGISTER) {
System.out.println("Socket UNREGISTER for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
} else if (be.type() == BridgeEventType.PUBLISH) {
System.out.println("Socket PUBLISH for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
} else if (be.type() == BridgeEventType.RECEIVE) {
System.out.println("Socket RECEIVE for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
} else if (be.type() == BridgeEventType.SEND) {
System.out.println("Socket SEND for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
} else if (be.type() == BridgeEventType.REGISTER) {
System.out.println("Socket REGISTER for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
eb.consumer("to-vertx").handler(message -> {
System.out.println("**** response to " + be.socket().webSession().id() + " msg = " + message.body());
if (message.body().toString().equals("CLOSE_SOCKET")) {
be.socket().close();
}
});
//gw.send(be.rawMessage().toString());
} else {
System.out.println("Message from: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
}
// System.out.println("USER=" + be.socket().webUser().principal());
be.complete(true);
});
// Create a router endpoint for the static content.
router.route().handler(StaticHandler.create());
// Start the web server and tell it to use the router to handle requests.
//vertx.createHttpServer(new HttpServerOptions().setSsl(true).setKeyStoreOptions(
// new JksOptions().setPath("server-keystore.jks").setPassword("wibble")
// )).requestHandler(router::accept).listen(3001);
vertx.createHttpServer().requestHandler(router::accept).listen(3001);
// Register to listen for messages coming IN to the server
eb.consumer("chat.to.server").handler(message -> {
// Create a timestamp string
String timestamp = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM).format(Date.from(Instant.now()));
// Send the message back out to all clients with the timestamp prepended.
gw.send("TO ECHO:" + timestamp + ": " + message.body());
// eb.publish("foofoofoo", message.body());
});
eb.consumer("to-vertx").handler(message -> {
eb.publish("chat.to.client", message.body());
});
// Serve the non private static pages
router.route().handler(StaticHandler.create());
}
}

View File

@ -1,44 +0,0 @@
package org.bigbluebutton.vertx;
import io.vertx.core.AbstractVerticle;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.StaticHandler;
import io.vertx.ext.bridge.BridgeEventType;
import io.vertx.ext.web.handler.sockjs.BridgeOptions;
import io.vertx.ext.web.handler.sockjs.PermittedOptions;
import io.vertx.ext.web.handler.sockjs.SockJSHandler;
public class RealtimeVerticle extends AbstractVerticle {
@Override
public void start() throws Exception {
Router router = Router.router(vertx);
// Allow outbound traffic to the news-feed address
BridgeOptions options = new BridgeOptions().addOutboundPermitted(new PermittedOptions().setAddress("news-feed"));
router.route("/eventbus/*").handler(SockJSHandler.create(vertx).bridge(options, event -> {
// You can also optionally provide a handler like this which will be passed any events that occur on the bridge
// You can use this for monitoring or logging, or to change the raw messages in-flight.
// It can also be used for fine grained access control.
if (event.type() == BridgeEventType.SOCKET_CREATED) {
System.out.println("A socket was created");
}
// This signals that it's ok to process the event
event.complete(true);
}));
// Serve the static resources
router.route().handler(StaticHandler.create());
vertx.createHttpServer().requestHandler(router::accept).listen(3000);
// Publish a message to the address "news-feed" every second
vertx.setPeriodic(1000, t -> vertx.eventBus().publish("news-feed", "news from the server!"));
}
}

View File

@ -1,87 +0,0 @@
package org.bigbluebutton.vertx;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import io.vertx.ext.web.handler.BodyHandler;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="http://tfox.org">Tim Fox</a>
*/
public class SimpleREST extends AbstractVerticle {
private Map<String, JsonObject> products = new HashMap<>();
@Override
public void start() {
setUpInitialData();
Router router = Router.router(vertx);
router.route().handler(BodyHandler.create());
router.get("/products/:productID").handler(this::handleGetProduct);
router.put("/products/:productID").handler(this::handleAddProduct);
router.get("/products").handler(this::handleListProducts);
vertx.createHttpServer().requestHandler(router::accept).listen(4000);
}
private void handleGetProduct(RoutingContext routingContext) {
String productID = routingContext.request().getParam("productID");
HttpServerResponse response = routingContext.response();
if (productID == null) {
sendError(400, response);
} else {
JsonObject product = products.get(productID);
if (product == null) {
sendError(404, response);
} else {
response.putHeader("content-type", "application/json").end(product.encodePrettily());
}
}
}
private void handleAddProduct(RoutingContext routingContext) {
String productID = routingContext.request().getParam("productID");
HttpServerResponse response = routingContext.response();
if (productID == null) {
sendError(400, response);
} else {
JsonObject product = routingContext.getBodyAsJson();
if (product == null) {
sendError(400, response);
} else {
products.put(productID, product);
response.end();
}
}
}
private void handleListProducts(RoutingContext routingContext) {
JsonArray arr = new JsonArray();
products.forEach((k, v) -> arr.add(v));
routingContext.response().putHeader("content-type", "application/json").end(arr.encodePrettily());
}
private void sendError(int statusCode, HttpServerResponse response) {
response.setStatusCode(statusCode).end();
}
private void setUpInitialData() {
addProduct(new JsonObject().put("id", "prod3568").put("name", "Egg Whisk").put("price", 3.99).put("weight", 150));
addProduct(new JsonObject().put("id", "prod7340").put("name", "Tea Cosy").put("price", 5.99).put("weight", 100));
addProduct(new JsonObject().put("id", "prod8643").put("name", "Spatula").put("price", 1.00).put("weight", 80));
}
private void addProduct(JsonObject product) {
products.put(product.getString("id"), product);
}
}

View File

@ -1,24 +0,0 @@
package org.bigbluebutton.vertx;
import io.vertx.ext.bridge.BridgeEventType;
import io.vertx.ext.web.handler.sockjs.BridgeOptions;
import io.vertx.ext.web.handler.sockjs.SockJSHandler;
public class SockJSHandlerFactory {
public SockJSHandler setupHandler(SockJSHandler ebHandler, BridgeOptions opts) {
ebHandler.bridge(opts, be -> {
if (be.type() == BridgeEventType.SOCKET_CREATED) {
System.out.println("Socket create for: " + be.socket().webSession().id());
} else if (be.type() == BridgeEventType.SOCKET_CLOSED) {
System.out.println("Socket closed for: " + be.socket().webSession().id());
} else {
System.out.println("Message from: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
}
be.complete(true);
});
return ebHandler;
}
}

View File

@ -1,125 +0,0 @@
package org.bigbluebutton.vertx;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.eventbus.EventBus;
import io.vertx.ext.bridge.BridgeEventType;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.BodyHandler;
import io.vertx.ext.web.handler.CookieHandler;
import io.vertx.ext.web.handler.SessionHandler;
import io.vertx.ext.web.handler.StaticHandler;
import io.vertx.ext.web.handler.sockjs.BridgeOptions;
import io.vertx.ext.web.handler.sockjs.PermittedOptions;
import io.vertx.ext.web.handler.sockjs.SockJSHandler;
import io.vertx.ext.web.handler.sockjs.SockJSHandlerOptions;
import io.vertx.ext.web.sstore.LocalSessionStore;
import org.bigbluebutton.ConnectionManager;
public class SockJSHandlerVerticle extends AbstractVerticle {
private final ConnectionManager gw;
public SockJSHandlerVerticle(ConnectionManager gw) {
this.gw = gw;
}
@Override
public void start() throws Exception {
Router router = Router.router(vertx);
// We need cookies, sessions and request bodies
router.route().handler(CookieHandler.create());
router.route().handler(BodyHandler.create());
router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));
// Simple auth service which uses a properties file for user/role info
//AuthProvider authProvider = new MyAuthProvider(vertx);
// We need a user session handler too to make sure the user is stored in the session between requests
//router.route().handler(UserSessionHandler.create(authProvider));
// PermittedOptions outboundPermitted2 = new PermittedOptions().setAddressRegex("to-server-.+");
// PermittedOptions inboundPermitted2 = new PermittedOptions().setAddressRegex("to-client-.+");
// Allow events for the designated addresses in/out of the event bus bridge
BridgeOptions opts = new BridgeOptions()
.addInboundPermitted(new PermittedOptions().setAddress("to-server"))
.addOutboundPermitted(new PermittedOptions().setAddressRegex("to-client-.+"));
SockJSHandlerOptions options = new SockJSHandlerOptions().setHeartbeatInterval(2000);
// Create the event bus bridge and add it to the router.
SockJSHandler sockJSHandler = SockJSHandler.create(vertx, options);
router.route("/eventbus/*").handler(sockJSHandler);
EventBus eb = vertx.eventBus();
sockJSHandler.bridge(opts, be -> {
if (be.type() == BridgeEventType.SOCKET_CREATED) {
System.out.println("Socket create for session: " + be.socket().webSession().id() + " socketWriteId:" + be.socket().writeHandlerID());
eb.consumer(be.socket().webSession().id()).handler(message -> {
be.socket().close();
});
gw.socketCreated(be.socket().webSession().id());
} else if (be.type() == BridgeEventType.SOCKET_CLOSED) {
System.out.println("Socket closed for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
gw.socketClosed(be.socket().webSession().id());
eb.consumer(be.socket().webSession().id()).unregister();
} else if (be.type() == BridgeEventType.SOCKET_IDLE) {
System.out.println("Socket SOCKET_IDLE for: " + be.socket().webSession().id());
} else if (be.type() == BridgeEventType.SOCKET_PING) {
System.out.println("Socket SOCKET_PING for: " + be.socket().webSession().id());
} else if (be.type() == BridgeEventType.UNREGISTER) {
System.out.println("Socket UNREGISTER for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
} else if (be.type() == BridgeEventType.PUBLISH) {
System.out.println("Socket PUBLISH for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
} else if (be.type() == BridgeEventType.RECEIVE) {
System.out.println("Msg to Client: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
} else if (be.type() == BridgeEventType.SEND) {
//System.out.println("Msg from Client: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
//String body = be.getRawMessage().getJsonObject("body").encode();
gw.onMessageReceived(be.socket().webSession().id(), be.getRawMessage().getJsonObject("body"));
} else if (be.type() == BridgeEventType.REGISTER) {
System.out.println("Socket REGISTER for: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
String address = be.getRawMessage().getString("address");
gw.register(be.socket().webSession().id(), address);
} else {
System.out.println("Message from: " + be.socket().webSession().id() + " \n " + be.getRawMessage());
}
// System.out.println("USER=" + be.socket().webUser().principal());
be.complete(true);
});
// Create a router endpoint for the static content.
router.route().handler(StaticHandler.create());
// Start the web server and tell it to use the router to handle requests.
//vertx.createHttpServer(new HttpServerOptions().setSsl(true).setKeyStoreOptions(
// new JksOptions().setPath("server-keystore.jks").setPassword("wibble")
// )).requestHandler(router::accept).listen(3001);
vertx.createHttpServer().requestHandler(router::accept).listen(3001);
// Register to listen for messages coming IN to the server
eb.consumer("to.server").handler(message -> {
// Create a timestamp string
// String timestamp = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM).format(Date.from(Instant.now()));
// Send the message back out to all clients with the timestamp prepended.
//gw.send("TO ECHO:" + timestamp + ": " + message.body());
// eb.publish("foofoofoo", message.body());
});
//eb.consumer("to-vertx").handler(message -> {
// eb.publish("chat.to.client", message.body());
//});
// Serve the non private static pages
router.route().handler(StaticHandler.create());
}
}

View File

@ -1,39 +0,0 @@
package org.bigbluebutton.vertx;
import io.vertx.core.Vertx;
import io.vertx.core.eventbus.Message;
import io.vertx.core.eventbus.MessageConsumer;
import io.vertx.core.json.JsonObject;
import org.bigbluebutton.VertxToAkkaGateway;
public class VertxToAkkaBus {
public VertxToAkkaBus(Vertx vertx, VertxToAkkaGateway gw) {
MessageConsumer<String> consumer =
vertx.eventBus().consumer("to-akka-gw");
consumer.handler(message -> {
System.out.println("I have received a message: " + message.body());
if (message.replyAddress() != null) {
String replyChannel = "reply-channel";
MessageConsumer<String> replyConsumer =
vertx.eventBus().consumer(replyChannel);
replyConsumer.handler(replyMessage -> {
System.out.println("Got Authenticated");
message.reply(replyMessage.body().toString());
replyConsumer.unregister();
});
gw.sendWithReply(message.body().toString(), replyChannel);
} else {
gw.send(message.body().toString());
}
});
}
}

View File

@ -1,24 +0,0 @@
package org.bigbluebutton
import io.vertx.core.Vertx
import akka.actor._
import akka.actor.ActorLogging
import org.bigbluebutton.vertx.IAkkaToVertxGateway
import org.bigbluebutton.vertx.AkkaToVertxGateway
object AuthService {
def props(gw: AkkaToVertxGateway): Props =
Props(classOf[AuthService], gw)
}
class AuthService(gw: AkkaToVertxGateway)
extends Actor with ActorLogging {
def receive = {
case msg: String => {
println("****** Authenticating " + msg)
sender ! "Let `em in!"
}
case _ => log.error("Cannot handle message ")
}
}

View File

@ -1,38 +0,0 @@
package org.bigbluebutton
import akka.actor.ActorSystem
import akka.event.Logging
import org.bigbluebutton.vertx.HelloWorld
import io.vertx.core.Vertx
import org.bigbluebutton.client.{ ClientGWApplication, MsgToClientGW }
import org.bigbluebutton.client.bus.InternalMessageBus
import org.bigbluebutton.vertx.AkkaToVertxGateway
import org.bigbluebutton.vertx.VertxToAkkaBus
object Boot extends App with SystemConfiguration {
implicit val system = ActorSystem("vertx-akka-system")
val log = Logging(system, getClass)
log.debug("*********** vertx-akka-system ***********************")
val vertx = Vertx.vertx()
val vertxGW = new AkkaToVertxGateway(vertx)
val echoActor = system.actorOf(EchoService.props(vertxGW, vertx), "echo-actor")
val authActor = system.actorOf(AuthService.props(vertxGW), "auth-actor")
val akkaGW = new VertxToAkkaGateway(system, vertx, authActor, echoActor)
val vertxToAkkaBus = new VertxToAkkaBus(vertx, akkaGW)
val connEventBus = new InternalMessageBus
val connectionManager = new ConnectionManager(system, vertx, connEventBus)
val clientManager = new ClientManager(system, connEventBus)
val msgToClientGW = new MsgToClientGW
val clientGW = new ClientGWApplication(system, msgToClientGW, connEventBus)
val hello = new HelloWorld(vertx, akkaGW, connectionManager)
hello.startup()
}

View File

@ -1,44 +0,0 @@
package org.bigbluebutton
import akka.actor.{ Actor, ActorLogging, ActorSystem, Props }
import org.bigbluebutton.client.bus.{ ConnectionCreated, ConnectionDestroyed, InternalMessageBus }
import org.bigbluebutton.client.Client
class ClientManager(system: ActorSystem, connEventBus: InternalMessageBus) {
val actorRef = system.actorOf(ClientManagerActor.props(connEventBus), "clientMgrActor")
}
object ClientManagerActor {
val CLIENT_MANAGER_CHANNEL = "client-manager-channel"
def props(connEventBus: InternalMessageBus): Props = Props(classOf[ClientManagerActor], connEventBus)
}
case class ClientManagerActor(connEventBus: InternalMessageBus) extends Actor with ActorLogging {
private var clients = new collection.immutable.HashMap[String, Client]
def receive = {
case m: ConnectionCreated =>
val client = Client(m.connInfo.connId, connEventBus)
clients += client.clientId -> client
client.actorRef forward (m)
case m: ConnectionDestroyed =>
val client = clients.get(m.connInfo.connId)
client foreach { u =>
clients -= m.connInfo.connId
u.actorRef forward (m)
}
case _ => log.debug("***** Connection cannot handle msg ")
}
override def preStart(): Unit = {
super.preStart()
connEventBus.subscribe(self, ClientManagerActor.CLIENT_MANAGER_CHANNEL)
}
override def postStop(): Unit = {
connEventBus.unsubscribe(self, ClientManagerActor.CLIENT_MANAGER_CHANNEL)
super.postStop()
}
}

View File

@ -1,124 +0,0 @@
package org.bigbluebutton
import akka.actor.{ Actor, ActorContext, ActorLogging, ActorRef, Props }
import io.vertx.core.{ Handler, Vertx }
import io.vertx.core.eventbus.{ Message, MessageConsumer }
import io.vertx.core.json.JsonObject
import org.bigbluebutton.client.bus.ConnInfo2
import org.bigbluebutton.client.bus._
object Connection {
def apply(connId: String, vertx: Vertx, connEventBus: InternalMessageBus)(implicit context: ActorContext): Connection = new Connection(connId, vertx, connEventBus)(context)
}
class Connection(val connId: String, vertx: Vertx, connEventBus: InternalMessageBus)(implicit val context: ActorContext) {
val actorRef = context.actorOf(ConnectionActor.props(connId, vertx, connEventBus), "connActor" + "-" + connId)
val consumer: MessageConsumer[JsonObject] = vertx.eventBus().consumer("from-socket-" + connId)
consumer.handler(new MyConnHandler(actorRef))
}
object ConnectionActor {
def props(connId: String, vertx: Vertx, connEventBus: InternalMessageBus): Props = Props(classOf[ConnectionActor], connId, vertx, connEventBus)
}
case class MsgFoo(msg: JsonObject)
class ConnectionActor(connId: String, vertx: Vertx, connEventBus: InternalMessageBus) extends Actor with ActorLogging {
var handshakeDone = false
var connInfo: Option[ConnInfo2] = None
var clientAddress: Option[String] = None
def receive = {
case m: SocketDestroyed =>
connInfo foreach { conn =>
connEventBus.publish(MsgFromConnBusMsg(ClientManagerActor.CLIENT_MANAGER_CHANNEL, ConnectionDestroyed(conn)))
}
context stop self
case m: SocketRegister =>
clientAddress = Some(m.channel)
case m: MsgFoo =>
if (!handshakeDone) {
for {
conn <- getConnInfo(m.msg)
} yield {
println("**************************** HANDSHAKE DONE *****************************")
handshakeDone = true
connInfo = Some(conn)
connEventBus.publish(MsgFromConnBusMsg(ClientManagerActor.CLIENT_MANAGER_CHANNEL, ConnectionCreated(conn)))
val response = buildHandshakeReply(conn.meetingId, conn.userId, conn.token)
vertx.eventBus().publish("to-client-" + conn.token, response)
}
} else {
//println("************ FORWARDING TO CLIENT ACTOR *****************************")
connInfo foreach { conn =>
//println("************ FORWARDING TO CLIENT ACTOR " + "clientActor-" + conn.connId + " *****************************")
connEventBus.publish(MsgFromConnBusMsg("clientActor-" + conn.connId, MsgFromConnMsg(conn, m.msg.encode())))
}
}
case m: MsgToConnMsg =>
//println("MsgToConnMsg " + m.json)
connInfo foreach { conn =>
val jsonObject = new JsonObject(m.json)
vertx.eventBus().publish("to-client-" + conn.token, jsonObject)
}
case _ => log.debug("***** Connection cannot handle msg ")
}
private def getConnInfo(msg: JsonObject): Option[ConnInfo2] = {
var conn: Option[ConnInfo2] = None
if (msg.containsKey("header") && msg.containsKey("body")) {
val header = msg.getJsonObject("header")
val body = msg.getJsonObject("body")
if (header.containsKey("name") && header.containsKey("meetingId")
&& header.containsKey("userId") && body.containsKey("token")) {
val meetingId = header.getString("meetingId")
val userId = header.getString("userId")
val token = body.getString("token")
conn = Some(new ConnInfo2(meetingId, userId, token, connId))
}
}
conn
}
private def buildHandshakeReply(meetingId: String, userId: String, token: String): JsonObject = {
val header: JsonObject = new JsonObject()
header.put("name", "HandshakeReplyMessage")
header.put("userId", userId)
header.put("meetingId", meetingId)
val body = new JsonObject()
body.put("token", token)
val reply = new JsonObject()
reply.put("header", header)
reply.put("body", body)
reply
}
override def preStart(): Unit = {
super.preStart()
connEventBus.subscribe(self, "connActor-" + connId)
}
override def postStop(): Unit = {
super.postStop()
connEventBus.unsubscribe(self, "connActor-" + connId)
}
}
class MyConnHandler(actorRef: ActorRef) extends Handler[Message[JsonObject]] {
def handle(message: Message[JsonObject]) = {
//println("My Handler " + message.body())
actorRef ! (MsgFoo(message.body()))
}
}

View File

@ -1,55 +0,0 @@
package org.bigbluebutton
import akka.actor.{ Actor, ActorContext, ActorLogging, ActorSystem, Props }
import io.vertx.core.Vertx
import io.vertx.core.json.JsonObject
import org.bigbluebutton.client.bus.InternalMessageBus
class ConnectionManager(system: ActorSystem, vertx: Vertx, connEventBus: InternalMessageBus) {
val actorRef = system.actorOf(ConnManagerActor.props(vertx, connEventBus), "connMgrActor")
def socketCreated(id: String): Unit = {
actorRef ! SocketCreated(id)
}
def socketClosed(id: String): Unit = {
actorRef ! SocketDestroyed(id)
}
def register(id: String, address: String): Unit = {
actorRef ! SocketRegister(id, address)
}
def onMessageReceived(id: String, msg: JsonObject): Unit = {
vertx.eventBus().publish("from-socket-" + id, msg)
}
}
case class SocketCreated(id: String)
case class SocketDestroyed(id: String)
case class SocketRegister(id: String, channel: String)
object ConnManagerActor {
def props(vertx: Vertx, connEventBus: InternalMessageBus): Props = Props(classOf[ConnManagerActor], vertx, connEventBus)
}
case class ConnManagerActor(vertx: Vertx, connEventBus: InternalMessageBus) extends Actor with ActorLogging {
private var conns = new collection.immutable.HashMap[String, Connection]
def receive = {
case m: SocketCreated =>
val conn = Connection(m.id, vertx, connEventBus)
conns += conn.connId -> conn
case m: SocketDestroyed =>
val conn = conns.get(m.id)
conn foreach { u =>
conns -= m.id
u.actorRef forward (m)
}
case m: SocketRegister =>
val conn = conns.get(m.id)
conn foreach (u => u.actorRef forward (m))
case _ => log.debug("***** Connection cannot handle msg ")
}
}

View File

@ -1,30 +0,0 @@
package org.bigbluebutton
import io.vertx.core.Vertx
import akka.actor._
import akka.actor.ActorLogging
import org.bigbluebutton.vertx.IAkkaToVertxGateway
import org.bigbluebutton.vertx.AkkaToVertxGateway
object EchoService {
def props(gw: AkkaToVertxGateway, vertx: Vertx): Props =
Props(classOf[EchoService], gw, vertx)
}
class EchoService(gw: AkkaToVertxGateway, vertx: Vertx) extends Actor with ActorLogging {
private var i: Int = 0;
def receive = {
case msg: String => {
//println("****** Echoing " + msg)
gw.send("FROM ECHO: " + msg)
i += 1
if (i > 50) {
//gw.send("CLOSE_SOCKET")
vertx.eventBus.publish("to-vertx", "CLOSE_SOCKET")
}
}
case _ => log.error("Cannot handle message ")
}
}

Some files were not shown because too many files have changed in this diff Show More