* VRFS-1790 - drag handles added to configure tracks; VRFS-1653 - fixed bug where slow initial connect would cause loop; also log in on initail connect instead of extra login message
This commit is contained in:
parent
4425d66484
commit
3d1c4f2488
|
|
@ -36,6 +36,7 @@ FactoryGirl.define do
|
|||
addr 0
|
||||
locidispid 0
|
||||
client_type 'client'
|
||||
sequence(:channel_id) { |n| "Channel#{n}"}
|
||||
association :user, factory: :user
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -162,3 +162,4 @@ indexing_for_regions.sql
|
|||
latency_tester.sql
|
||||
fix_users_location_fields.sql
|
||||
audio_latency.sql
|
||||
connection_channel_id.sql
|
||||
|
|
@ -0,0 +1 @@
|
|||
ALTER TABLE connections ADD COLUMN channel_id VARCHAR(256) NOT NULL;
|
||||
|
|
@ -77,6 +77,7 @@ message ClientMessage {
|
|||
SERVER_REJECTION_ERROR = 1005;
|
||||
SERVER_PERMISSION_ERROR = 1010;
|
||||
SERVER_BAD_STATE_ERROR = 1015;
|
||||
SERVER_DUPLICATE_CLIENT_ERROR = 1016;
|
||||
}
|
||||
|
||||
// Identifies which inner message is filled in
|
||||
|
|
@ -159,6 +160,7 @@ message ClientMessage {
|
|||
optional ServerRejectionError server_rejection_error = 1005;
|
||||
optional ServerPermissionError server_permission_error = 1010;
|
||||
optional ServerBadStateError server_bad_state_error = 1015;
|
||||
optional ServerDuplicateClientError server_duplicate_client_error = 1016;
|
||||
}
|
||||
|
||||
// route_to: server
|
||||
|
|
@ -529,4 +531,9 @@ message ServerBadStateError {
|
|||
optional string error_msg = 1;
|
||||
}
|
||||
|
||||
// route_to: client
|
||||
// this indicates that we detected another client with the same client ID
|
||||
message ServerDuplicateClientError {
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ module JamRuby
|
|||
end
|
||||
|
||||
# reclaim the existing connection, if ip_address is not nil then perhaps a new address as well
|
||||
def reconnect(conn, reconnect_music_session_id, ip_address, connection_stale_time, connection_expire_time)
|
||||
def reconnect(conn, channel_id, reconnect_music_session_id, ip_address, connection_stale_time, connection_expire_time)
|
||||
music_session_id = nil
|
||||
reconnected = false
|
||||
|
||||
|
|
@ -86,7 +86,7 @@ module JamRuby
|
|||
end
|
||||
|
||||
sql =<<SQL
|
||||
UPDATE connections SET (aasm_state, updated_at, music_session_id, joined_session_at, stale_time, expire_time) = ('#{Connection::CONNECT_STATE.to_s}', NOW(), #{music_session_id_expression}, #{joined_session_at_expression}, #{connection_stale_time}, #{connection_expire_time})
|
||||
UPDATE connections SET (channel_id, aasm_state, updated_at, music_session_id, joined_session_at, stale_time, expire_time) = ('#{channel_id}', '#{Connection::CONNECT_STATE.to_s}', NOW(), #{music_session_id_expression}, #{joined_session_at_expression}, #{connection_stale_time}, #{connection_expire_time})
|
||||
WHERE
|
||||
client_id = '#{conn.client_id}'
|
||||
RETURNING music_session_id
|
||||
|
|
@ -184,7 +184,7 @@ SQL
|
|||
# this number is used by notification logic elsewhere to know
|
||||
# 'oh the user joined for the 1st time, so send a friend update', or
|
||||
# 'don't bother because the user has connected somewhere else already'
|
||||
def create_connection(user_id, client_id, ip_address, client_type, connection_stale_time, connection_expire_time, &blk)
|
||||
def create_connection(user_id, client_id, channel_id, ip_address, client_type, connection_stale_time, connection_expire_time, &blk)
|
||||
|
||||
# validate client_type
|
||||
raise "invalid client_type: #{client_type}" if client_type != 'client' && client_type != 'browser'
|
||||
|
|
@ -216,8 +216,8 @@ SQL
|
|||
|
||||
lock_connections(conn)
|
||||
|
||||
conn.exec("INSERT INTO connections (user_id, client_id, ip_address, client_type, addr, locidispid, aasm_state, stale_time, expire_time) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
[user_id, client_id, ip_address, client_type, addr, locidispid, Connection::CONNECT_STATE.to_s, connection_stale_time, connection_expire_time]).clear
|
||||
conn.exec("INSERT INTO connections (user_id, client_id, channel_id, ip_address, client_type, addr, locidispid, aasm_state, stale_time, expire_time) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
|
||||
[user_id, client_id, channel_id, ip_address, client_type, addr, locidispid, Connection::CONNECT_STATE.to_s, connection_stale_time, connection_expire_time]).clear
|
||||
|
||||
# we just created a new connection-if this is the first time the user has shown up, we need to send out a message to his friends
|
||||
conn.exec("SELECT count(user_id) FROM connections WHERE user_id = $1", [user_id]) do |result|
|
||||
|
|
|
|||
|
|
@ -222,6 +222,17 @@ module JamRuby
|
|||
)
|
||||
end
|
||||
|
||||
# create a server bad state error
|
||||
def server_duplicate_client_error
|
||||
error = Jampb::ServerDuplicateClientError.new()
|
||||
|
||||
Jampb::ClientMessage.new(
|
||||
:type => ClientMessage::Type::SERVER_DUPLICATE_CLIENT_ERROR,
|
||||
:route_to => CLIENT_TARGET,
|
||||
:server_duplicate_client_error => error
|
||||
)
|
||||
end
|
||||
|
||||
###################################### NOTIFICATIONS ######################################
|
||||
|
||||
# create a friend update message
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ module JamRuby
|
|||
# or bootstrap a new latency_tester
|
||||
def self.connect(options)
|
||||
client_id = options[:client_id]
|
||||
channel_id = options[:channel_id]
|
||||
ip_address = options[:ip_address]
|
||||
connection_stale_time = options[:connection_stale_time]
|
||||
connection_expire_time = options[:connection_expire_time]
|
||||
|
|
@ -69,6 +70,7 @@ module JamRuby
|
|||
connection.stale_time = connection_stale_time
|
||||
connection.expire_time = connection_expire_time
|
||||
connection.as_musician = false
|
||||
connection.channel_id = channel_id
|
||||
unless connection.save
|
||||
return connection
|
||||
end
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@ FactoryGirl.define do
|
|||
addr 0
|
||||
locidispid 0
|
||||
client_type 'client'
|
||||
sequence(:channel_id) { |n| "Channel#{n}"}
|
||||
association :user, factory: :user
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ describe ConnectionManager do
|
|||
STALE_BUT_NOT_EXPIRED = 50
|
||||
DEFINITELY_EXPIRED = 70
|
||||
|
||||
let(:channel_id) {'1'}
|
||||
|
||||
before do
|
||||
@conn = PG::Connection.new(:dbname => SpecDb::TEST_DB_NAME, :user => "postgres", :password => "postgres", :host => "localhost")
|
||||
@connman = ConnectionManager.new(:conn => @conn)
|
||||
|
|
@ -46,8 +48,8 @@ describe ConnectionManager do
|
|||
user.save!
|
||||
user = nil
|
||||
|
||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
expect { @connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) }.to raise_error(PG::Error)
|
||||
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
expect { @connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME) }.to raise_error(PG::Error)
|
||||
end
|
||||
|
||||
it "create connection then delete it" do
|
||||
|
|
@ -56,7 +58,7 @@ describe ConnectionManager do
|
|||
#user_id = create_user("test", "user2", "user2@jamkazam.com")
|
||||
user = FactoryGirl.create(:user)
|
||||
|
||||
count = @connman.create_connection(user.id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
count = @connman.create_connection(user.id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
|
||||
count.should == 1
|
||||
|
||||
|
|
@ -86,7 +88,7 @@ describe ConnectionManager do
|
|||
#user_id = create_user("test", "user2", "user2@jamkazam.com")
|
||||
user = FactoryGirl.create(:user)
|
||||
|
||||
count = @connman.create_connection(user.id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
count = @connman.create_connection(user.id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
|
||||
count.should == 1
|
||||
|
||||
|
|
@ -102,7 +104,7 @@ describe ConnectionManager do
|
|||
cc.addr.should == 0x01010101
|
||||
cc.locidispid.should == 17192000002
|
||||
|
||||
@connman.reconnect(cc, nil, "33.1.2.3", STALE_TIME, EXPIRE_TIME)
|
||||
@connman.reconnect(cc, channel_id, nil, "33.1.2.3", STALE_TIME, EXPIRE_TIME)
|
||||
|
||||
cc = Connection.find_by_client_id!(client_id)
|
||||
cc.connected?.should be_true
|
||||
|
|
@ -215,7 +217,7 @@ describe ConnectionManager do
|
|||
it "flag stale connection" do
|
||||
client_id = "client_id8"
|
||||
user_id = create_user("test", "user8", "user8@jamkazam.com")
|
||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
|
||||
num = JamRuby::Connection.count(:conditions => ['aasm_state = ?','connected'])
|
||||
num.should == 1
|
||||
|
|
@ -256,7 +258,7 @@ describe ConnectionManager do
|
|||
it "expires stale connection" do
|
||||
client_id = "client_id8"
|
||||
user_id = create_user("test", "user8", "user8@jamkazam.com")
|
||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
|
||||
conn = Connection.find_by_client_id(client_id)
|
||||
set_updated_at(conn, Time.now - STALE_BUT_NOT_EXPIRED)
|
||||
|
|
@ -282,7 +284,7 @@ describe ConnectionManager do
|
|||
music_session_id = music_session.id
|
||||
user = User.find(user_id)
|
||||
|
||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
connection = @connman.join_music_session(user, client_id, music_session, true, TRACKS, 10)
|
||||
|
||||
connection.errors.any?.should be_false
|
||||
|
|
@ -318,8 +320,8 @@ describe ConnectionManager do
|
|||
client_id2 = "client_id10.12"
|
||||
user_id = create_user("test", "user10.11", "user10.11@jamkazam.com", :musician => true)
|
||||
user_id2 = create_user("test", "user10.12", "user10.12@jamkazam.com", :musician => false)
|
||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.create_connection(user_id2, client_id2, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.create_connection(user_id2, client_id2, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
|
||||
music_session = FactoryGirl.create(:active_music_session, user_id: user_id)
|
||||
music_session_id = music_session.id
|
||||
|
|
@ -338,7 +340,7 @@ describe ConnectionManager do
|
|||
client_id = "client_id10.2"
|
||||
|
||||
user_id = create_user("test", "user10.2", "user10.2@jamkazam.com")
|
||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
|
||||
music_session = FactoryGirl.create(:active_music_session, user_id: user_id)
|
||||
user = User.find(user_id)
|
||||
|
|
@ -354,8 +356,8 @@ describe ConnectionManager do
|
|||
fan_client_id = "client_id10.4"
|
||||
musician_id = create_user("test", "user10.3", "user10.3@jamkazam.com")
|
||||
fan_id = create_user("test", "user10.4", "user10.4@jamkazam.com", :musician => false)
|
||||
@connman.create_connection(musician_id, musician_client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.create_connection(fan_id, fan_client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.create_connection(musician_id, musician_client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.create_connection(fan_id, fan_client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
|
||||
music_session = FactoryGirl.create(:active_music_session, :fan_access => false, user_id: musician_id)
|
||||
music_session_id = music_session.id
|
||||
|
|
@ -379,7 +381,7 @@ describe ConnectionManager do
|
|||
music_session_id = music_session.id
|
||||
user = User.find(user_id2)
|
||||
|
||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
# specify real user id, but not associated with this session
|
||||
expect { @connman.join_music_session(user, client_id, music_session, true, TRACKS, 10) } .to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
|
|
@ -391,7 +393,7 @@ describe ConnectionManager do
|
|||
user = User.find(user_id)
|
||||
music_session = ActiveMusicSession.new
|
||||
|
||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
connection = @connman.join_music_session(user, client_id, music_session, true, TRACKS, 10)
|
||||
connection.errors.size.should == 1
|
||||
connection.errors.get(:music_session).should == [ValidationMessages::MUSIC_SESSION_MUST_BE_SPECIFIED]
|
||||
|
|
@ -405,7 +407,7 @@ describe ConnectionManager do
|
|||
music_session_id = music_session.id
|
||||
user = User.find(user_id2)
|
||||
|
||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
# specify real user id, but not associated with this session
|
||||
expect { @connman.join_music_session(user, client_id, music_session, true, TRACKS, 10) } .to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
|
|
@ -419,7 +421,7 @@ describe ConnectionManager do
|
|||
user = User.find(user_id)
|
||||
dummy_music_session = ActiveMusicSession.new
|
||||
|
||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
|
||||
expect { @connman.leave_music_session(user, Connection.find_by_client_id(client_id), dummy_music_session) }.to raise_error(JamRuby::StateError)
|
||||
end
|
||||
|
|
@ -434,7 +436,7 @@ describe ConnectionManager do
|
|||
|
||||
dummy_music_session = ActiveMusicSession.new
|
||||
|
||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.join_music_session(user, client_id, music_session, true, TRACKS, 10)
|
||||
expect { @connman.leave_music_session(user, Connection.find_by_client_id(client_id), dummy_music_session) }.to raise_error(JamRuby::StateError)
|
||||
end
|
||||
|
|
@ -447,7 +449,7 @@ describe ConnectionManager do
|
|||
music_session_id = music_session.id
|
||||
user = User.find(user_id)
|
||||
|
||||
@connman.create_connection(user_id, client_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.create_connection(user_id, client_id, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.join_music_session(user, client_id, music_session, true, TRACKS, 10)
|
||||
|
||||
assert_session_exists(music_session_id, true)
|
||||
|
|
@ -490,7 +492,7 @@ describe ConnectionManager do
|
|||
user = User.find(user_id)
|
||||
|
||||
client_id1 = Faker::Number.number(20)
|
||||
@connman.create_connection(user_id, client_id1, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
@connman.create_connection(user_id, client_id1, channel_id, "1.1.1.1", 'client', STALE_TIME, EXPIRE_TIME)
|
||||
music_session1 = FactoryGirl.create(:active_music_session, :user_id => user_id)
|
||||
connection1 = @connman.join_music_session(user, client_id1, music_session1, true, TRACKS, 10)
|
||||
connection1.errors.size.should == 0
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ require 'spec_helper'
|
|||
|
||||
describe LatencyTester do
|
||||
|
||||
let(:params) {{client_id: 'abc', ip_address: '10.1.1.1', connection_stale_time:40, connection_expire_time:60} }
|
||||
let(:params) {{client_id: 'abc', ip_address: '10.1.1.1', connection_stale_time:40, connection_expire_time:60, channel_id: '1'} }
|
||||
|
||||
it "success" do
|
||||
latency_tester = FactoryGirl.create(:latency_tester)
|
||||
|
|
|
|||
|
|
@ -66,7 +66,8 @@
|
|||
SERVER_BAD_STATE_RECOVERED: "SERVER_BAD_STATE_RECOVERED",
|
||||
SERVER_GENERIC_ERROR : "SERVER_GENERIC_ERROR",
|
||||
SERVER_REJECTION_ERROR : "SERVER_REJECTION_ERROR",
|
||||
SERVER_BAD_STATE_ERROR : "SERVER_BAD_STATE_ERROR"
|
||||
SERVER_BAD_STATE_ERROR : "SERVER_BAD_STATE_ERROR",
|
||||
SERVER_DUPLICATE_CLIENT_ERROR : "SERVER_DUPLICATE_CLIENT_ERROR"
|
||||
};
|
||||
|
||||
var route_to = context.JK.RouteToPrefix = {
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@
|
|||
heartbeatAckCheckInterval = null;
|
||||
}
|
||||
|
||||
if (!server.reconnecting) {
|
||||
if (!server.reconnecting && !server.noReconnect) {
|
||||
server.reconnecting = true;
|
||||
|
||||
var result = activeElementEvent('beforeDisconnect');
|
||||
|
|
@ -170,6 +170,8 @@
|
|||
|
||||
function loggedIn(header, payload) {
|
||||
|
||||
server.reconnecting = false;
|
||||
|
||||
if (!connectTimeout) {
|
||||
clearTimeout(connectTimeout);
|
||||
connectTimeout = null;
|
||||
|
|
@ -271,7 +273,6 @@
|
|||
else {
|
||||
window.location.reload();
|
||||
}
|
||||
server.reconnecting = false;
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -453,7 +454,18 @@
|
|||
connectDeferred = new $.Deferred();
|
||||
channelId = context.JK.generateUUID(); // create a new channel ID for every websocket connection
|
||||
|
||||
var uri = context.gon.websocket_gateway_uri + '?channel_id=' + channelId; // Set in index.html.erb.
|
||||
// we will log in one of 3 ways:
|
||||
// browser: use session cookie, and auth with token
|
||||
// native: use session cookie, and use the token
|
||||
// latency_tester: ask for client ID from backend; no token (trusted)
|
||||
var params = {
|
||||
channel_id: channelId,
|
||||
token: $.cookie("remember_token"),
|
||||
client_type: isClientMode() ? context.JK.clientType : 'latency_tester',
|
||||
client_id: isClientMode() ? (gon.global.env == "development" ? $.cookie('client_id') : null): context.jamClient.clientID
|
||||
}
|
||||
|
||||
var uri = context.gon.websocket_gateway_uri + '?' + $.param(params); // Set in index.html.erb.
|
||||
|
||||
logger.debug("connecting websocket: " + uri);
|
||||
|
||||
|
|
@ -499,12 +511,7 @@
|
|||
server.onOpen = function () {
|
||||
logger.debug("server.onOpen");
|
||||
|
||||
if(isClientMode()) {
|
||||
server.rememberLogin();
|
||||
}
|
||||
else {
|
||||
server.latencyTesterLogin();
|
||||
}
|
||||
// we should receive LOGIN_ACK very soon. we already set a timer elsewhere to give 4 seconds to receive it
|
||||
};
|
||||
|
||||
server.onMessage = function (e) {
|
||||
|
|
|
|||
|
|
@ -67,7 +67,9 @@
|
|||
.css('min-width', $channel.width())
|
||||
.css('min-height', $channel.height())
|
||||
.css('z-index', 10000)
|
||||
.data('original', $channel)
|
||||
.css('background-position', '4px 6px')
|
||||
.css('padding-left', '14px')
|
||||
.data('original', $channel);
|
||||
|
||||
$channel.data('cloned', hoverChannel);
|
||||
hoverChannel
|
||||
|
|
@ -120,6 +122,8 @@
|
|||
.css('width', '')
|
||||
.css('background-color', '')
|
||||
.css('padding', '')
|
||||
.css('padding-left', '')
|
||||
.css('background-position', '')
|
||||
.css('border', '')
|
||||
.css('border-radius', '')
|
||||
.css('right', '')
|
||||
|
|
|
|||
|
|
@ -94,22 +94,30 @@
|
|||
context.jamClient.OnDownloadAvailable();
|
||||
}
|
||||
|
||||
/**
|
||||
* This most likely occurs when multiple tabs in the same browser are open, until we make a fix for this...
|
||||
*/
|
||||
function duplicateClientError() {
|
||||
context.JK.Banner.showAlert("Duplicate Window (Development Mode Only)", "You have logged in another window in this browser. This window will continue to work but with degraded functionality. This limitation will soon be fixed.")
|
||||
context.JK.JamServer.noReconnect = true;
|
||||
}
|
||||
|
||||
function registerBadStateError() {
|
||||
logger.debug("register for server_bad_state_error");
|
||||
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.SERVER_BAD_STATE_ERROR, serverBadStateError);
|
||||
}
|
||||
|
||||
function registerBadStateRecovered() {
|
||||
logger.debug("register for server_bad_state_recovered");
|
||||
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.SERVER_BAD_STATE_RECOVERED, serverBadStateRecovered);
|
||||
}
|
||||
|
||||
function registerDownloadAvailable() {
|
||||
logger.debug("register for download_available");
|
||||
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.DOWNLOAD_AVAILABLE, downloadAvailable);
|
||||
}
|
||||
|
||||
function registerDuplicateClientError() {
|
||||
context.JK.JamServer.registerMessageCallback(context.JK.MessageType.SERVER_DUPLICATE_CLIENT_ERROR, duplicateClientError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic error handler for Ajax calls.
|
||||
*/
|
||||
|
|
@ -330,6 +338,7 @@
|
|||
registerBadStateRecovered();
|
||||
registerBadStateError();
|
||||
registerDownloadAvailable();
|
||||
registerDuplicateClientError();
|
||||
context.JK.FaderHelpers.initialize();
|
||||
context.window.onunload = this.unloadFunction;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
.channels-holder {
|
||||
.ftue-input {
|
||||
background-position:4px 6px;
|
||||
padding-left:14px;
|
||||
}
|
||||
}
|
||||
.unassigned-input-channels {
|
||||
min-height: 240px;
|
||||
overflow-y: auto;
|
||||
|
|
@ -39,6 +45,7 @@
|
|||
}
|
||||
}
|
||||
.ftue-input {
|
||||
|
||||
}
|
||||
|
||||
&.drag-in-progress {
|
||||
|
|
@ -84,6 +91,7 @@
|
|||
font-size: 12px;
|
||||
cursor: move;
|
||||
padding: 4px;
|
||||
padding-left:10px;
|
||||
border: solid 1px #999;
|
||||
margin-bottom: 15px;
|
||||
white-space: nowrap;
|
||||
|
|
@ -91,6 +99,9 @@
|
|||
text-overflow: ellipsis;
|
||||
text-align: left;
|
||||
line-height:20px;
|
||||
background-image:url('/assets/content/icon_drag_handle.png');
|
||||
background-position:0 3px;
|
||||
background-repeat:no-repeat;
|
||||
//direction: rtl;
|
||||
&.ui-draggable-dragging {
|
||||
margin-bottom: 0;
|
||||
|
|
@ -126,10 +137,13 @@
|
|||
|
||||
.ftue-input {
|
||||
padding: 0;
|
||||
padding-left:10px;
|
||||
border: 0;
|
||||
margin-bottom: 0;
|
||||
&.ui-draggable-dragging {
|
||||
padding: 4px;
|
||||
padding-left:14px;
|
||||
background-position:4px 6px;
|
||||
border: solid 1px #999;
|
||||
overflow: visible;
|
||||
}
|
||||
|
|
@ -163,7 +177,7 @@
|
|||
}*/
|
||||
}
|
||||
&:nth-of-type(2) {
|
||||
padding-left:2px;
|
||||
padding-left:10px;
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,6 +130,7 @@ FactoryGirl.define do
|
|||
addr 0
|
||||
locidispid 0
|
||||
client_type 'client'
|
||||
sequence(:channel_id) { |n| "Channel#{n}"}
|
||||
end
|
||||
|
||||
factory :friendship, :class => JamRuby::Friendship do
|
||||
|
|
|
|||
|
|
@ -11,22 +11,6 @@ module EventMachine
|
|||
module WebSocket
|
||||
class Connection < EventMachine::Connection
|
||||
attr_accessor :encode_json, :channel_id, :client_id, :user_id, :context, :trusted # client_id is uuid we give to each client to track them as we like
|
||||
|
||||
# http://stackoverflow.com/questions/11150147/how-to-check-if-eventmachineconnection-is-open
|
||||
attr_accessor :connected
|
||||
def connection_completed
|
||||
connected = true
|
||||
super
|
||||
end
|
||||
|
||||
def connected?
|
||||
!!connected
|
||||
end
|
||||
|
||||
def unbind
|
||||
connected = false
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -226,6 +210,40 @@ module JamWebsockets
|
|||
MQRouter.client_exchange = @clients_exchange
|
||||
end
|
||||
|
||||
# this method allows you to translate exceptions into websocket channel messages and behavior safely.
|
||||
# pass in your block, throw an error in your logic, and have the right things happen on the websocket channel
|
||||
def websocket_comm(client, original_message_id, &blk)
|
||||
begin
|
||||
blk.call
|
||||
rescue SessionError => e
|
||||
@log.info "ending client session deliberately due to malformed client behavior. reason=#{e}"
|
||||
begin
|
||||
# wrap the message up and send it down
|
||||
error_msg = @message_factory.server_rejection_error(e.to_s)
|
||||
send_to_client(client, error_msg)
|
||||
ensure
|
||||
cleanup_client(client)
|
||||
end
|
||||
rescue PermissionError => e
|
||||
@log.info "permission error. reason=#{e.to_s}"
|
||||
@log.info e
|
||||
|
||||
# wrap the message up and send it down
|
||||
error_msg = @message_factory.server_permission_error(original_message_id, e.to_s)
|
||||
send_to_client(client, error_msg)
|
||||
rescue => e
|
||||
@log.error "ending client session due to server programming or runtime error. reason=#{e.to_s}"
|
||||
@log.error e
|
||||
|
||||
begin
|
||||
# wrap the message up and send it down
|
||||
error_msg = @message_factory.server_generic_error(e.to_s)
|
||||
send_to_client(client, error_msg)
|
||||
ensure
|
||||
cleanup_client(client)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def new_client(client, is_trusted)
|
||||
# default to using json instead of pb
|
||||
|
|
@ -246,6 +264,9 @@ module JamWebsockets
|
|||
client.encode_json = false
|
||||
end
|
||||
|
||||
websocket_comm(client, nil) do
|
||||
handle_login(client, handshake.query)
|
||||
end
|
||||
}
|
||||
|
||||
client.onclose {
|
||||
|
|
@ -261,50 +282,26 @@ module JamWebsockets
|
|||
end
|
||||
}
|
||||
|
||||
client.onmessage { |msg|
|
||||
client.onmessage { |data|
|
||||
|
||||
# TODO: set a max message size before we put it through PB?
|
||||
# TODO: rate limit?
|
||||
|
||||
pb_msg = nil
|
||||
msg = nil
|
||||
|
||||
begin
|
||||
# extract the message safely
|
||||
websocket_comm(client, nil) do
|
||||
if client.encode_json
|
||||
#example: {"type":"LOGIN", "target":"server", "login" : {"username":"hi"}}
|
||||
parse = JSON.parse(msg)
|
||||
pb_msg = Jampb::ClientMessage.json_create(parse)
|
||||
self.route(pb_msg, client)
|
||||
json = JSON.parse(data)
|
||||
msg = Jampb::ClientMessage.json_create(json)
|
||||
else
|
||||
pb_msg = Jampb::ClientMessage.parse(msg.to_s)
|
||||
self.route(pb_msg, client)
|
||||
msg = Jampb::ClientMessage.parse(data.to_s)
|
||||
end
|
||||
rescue SessionError => e
|
||||
@log.info "ending client session deliberately due to malformed client behavior. reason=#{e}"
|
||||
begin
|
||||
# wrap the message up and send it down
|
||||
error_msg = @message_factory.server_rejection_error(e.to_s)
|
||||
send_to_client(client, error_msg)
|
||||
ensure
|
||||
cleanup_client(client)
|
||||
end
|
||||
rescue PermissionError => e
|
||||
@log.info "permission error. reason=#{e.to_s}"
|
||||
@log.info e
|
||||
end
|
||||
|
||||
# wrap the message up and send it down
|
||||
error_msg = @message_factory.server_permission_error(pb_msg.message_id, e.to_s)
|
||||
send_to_client(client, error_msg)
|
||||
rescue => e
|
||||
@log.error "ending client session due to server programming or runtime error. reason=#{e.to_s}"
|
||||
@log.error e
|
||||
|
||||
begin
|
||||
# wrap the message up and send it down
|
||||
error_msg = @message_factory.server_generic_error(e.to_s)
|
||||
send_to_client(client, error_msg)
|
||||
ensure
|
||||
cleanup_client(client)
|
||||
end
|
||||
# then route it internally
|
||||
websocket_comm(client, msg.message_id) do
|
||||
self.route(msg, client)
|
||||
end
|
||||
}
|
||||
end
|
||||
|
|
@ -391,9 +388,9 @@ module JamWebsockets
|
|||
|
||||
# removes all resources associated with a client
|
||||
def cleanup_client(client)
|
||||
@semaphore.synchronize do
|
||||
client.close if client.connected?
|
||||
client.close
|
||||
|
||||
@semaphore.synchronize do
|
||||
pending = client.context.nil? # presence of context implies this connection has been logged into
|
||||
|
||||
if pending
|
||||
|
|
@ -514,6 +511,7 @@ module JamWebsockets
|
|||
heartbeat_interval, connection_stale_time, connection_expire_time = determine_connection_times(nil, client_type)
|
||||
latency_tester = LatencyTester.connect({
|
||||
client_id: client_id,
|
||||
channel_id: client.channel_id,
|
||||
ip_address: remote_ip,
|
||||
connection_stale_time: connection_stale_time,
|
||||
connection_expire_time: connection_expire_time})
|
||||
|
|
@ -543,15 +541,15 @@ module JamWebsockets
|
|||
end
|
||||
end
|
||||
|
||||
def handle_login(login, client)
|
||||
username = login.username if login.value_for_tag(1)
|
||||
password = login.password if login.value_for_tag(2)
|
||||
token = login.token if login.value_for_tag(3)
|
||||
client_id = login.client_id if login.value_for_tag(4)
|
||||
reconnect_music_session_id = login.reconnect_music_session_id if login.value_for_tag(5)
|
||||
client_type = login.client_type if login.value_for_tag(6)
|
||||
def handle_login(client, options)
|
||||
username = options["username"]
|
||||
password = options["password"]
|
||||
token = options["token"]
|
||||
client_id = options["client_id"]
|
||||
reconnect_music_session_id = options["music_session_id"]
|
||||
client_type = options["client_type"]
|
||||
|
||||
@log.info("*** handle_login: token=#{token}; client_id=#{client_id}, client_type=#{client_type}")
|
||||
@log.info("handle_login: client_type=#{client_type} token=#{token} client_id=#{client_id} channel_id=#{client.channel_id}")
|
||||
|
||||
if client_type == Connection::TYPE_LATENCY_TESTER
|
||||
handle_latency_tester_login(client_id, client_type, client)
|
||||
|
|
@ -568,17 +566,21 @@ module JamWebsockets
|
|||
|
||||
user = valid_login(username, password, token, client_id)
|
||||
|
||||
# XXX This logic needs to instead be handled by a broadcast out to all websockets indicating dup
|
||||
# kill any websocket connections that have this same client_id, which can happen in race conditions
|
||||
# this code must happen here, before we go any further, so that there is only one websocket connection per client_id
|
||||
existing_context = @client_lookup[client_id]
|
||||
if existing_context
|
||||
# in some reconnect scenarios, we may have in memory a websocket client still.
|
||||
# in some reconnect scenarios, we may have in memory a websocket client still.
|
||||
# let's whack it, and tell the other client, if still connected, that this is a duplicate login attempt
|
||||
@log.info "duplicate client: #{existing_context}"
|
||||
Diagnostic.duplicate_client(existing_context.user, existing_context) if existing_context.client.connected
|
||||
Diagnostic.duplicate_client(existing_context.user, existing_context)
|
||||
error_msg = @message_factory.server_duplicate_client_error
|
||||
send_to_client(existing_context.client, error_msg)
|
||||
cleanup_client(existing_context.client)
|
||||
end
|
||||
|
||||
connection = JamRuby::Connection.find_by_client_id(client_id)
|
||||
connection = Connection.find_by_client_id(client_id)
|
||||
# if this connection is reused by a different user (possible in logout/login scenarios), then whack the connection
|
||||
# because it will recreate a new connection lower down
|
||||
if connection && user && connection.user != user
|
||||
|
|
@ -608,7 +610,7 @@ module JamWebsockets
|
|||
recording_id = nil
|
||||
|
||||
ConnectionManager.active_record_transaction do |connection_manager|
|
||||
music_session_id, reconnected = connection_manager.reconnect(connection, reconnect_music_session_id, remote_ip, connection_stale_time, connection_expire_time)
|
||||
music_session_id, reconnected = connection_manager.reconnect(connection, client.channel_id, reconnect_music_session_id, remote_ip, connection_stale_time, connection_expire_time)
|
||||
|
||||
if music_session_id.nil?
|
||||
# if this is a reclaim of a connection, but music_session_id comes back null, then we need to check if this connection was IN a music session before.
|
||||
|
|
@ -644,7 +646,7 @@ module JamWebsockets
|
|||
unless connection
|
||||
# log this connection in the database
|
||||
ConnectionManager.active_record_transaction do |connection_manager|
|
||||
connection_manager.create_connection(user.id, client.client_id, remote_ip, client_type, connection_stale_time, connection_expire_time) do |conn, count|
|
||||
connection_manager.create_connection(user.id, client.client_id, client.channel_id, remote_ip, client_type, connection_stale_time, connection_expire_time) do |conn, count|
|
||||
if count == 1
|
||||
Notification.send_friend_update(user.id, true, conn)
|
||||
end
|
||||
|
|
@ -755,7 +757,7 @@ module JamWebsockets
|
|||
if !token.nil? && token != ''
|
||||
@log.debug "logging in via token"
|
||||
# attempt login with token
|
||||
user = JamRuby::User.find_by_remember_token(token)
|
||||
user = User.find_by_remember_token(token)
|
||||
|
||||
if user.nil?
|
||||
@log.debug "no user found with token #{token}"
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ FactoryGirl.define do
|
|||
ip_address '1.1.1.1'
|
||||
as_musician true
|
||||
client_type 'client'
|
||||
sequence(:channel_id) { |n| "Channel#{n}"}
|
||||
end
|
||||
|
||||
factory :instrument, :class => JamRuby::Instrument do
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ end
|
|||
|
||||
|
||||
# does a login and returns client
|
||||
def login(router, user, password, client_id)
|
||||
def login(router, user, password, client_id, token, client_type)
|
||||
|
||||
message_factory = MessageFactory.new
|
||||
client = LoginClient.new
|
||||
|
|
@ -60,15 +60,9 @@ def login(router, user, password, client_id)
|
|||
|
||||
@router.new_client(client, false)
|
||||
handshake = double("handshake")
|
||||
handshake.should_receive(:query).twice.and_return({ "pb" => "true", "channel_id" => SecureRandom.uuid })
|
||||
handshake.should_receive(:query).exactly(3).times.and_return({ "pb" => "true", "channel_id" => SecureRandom.uuid, "client_id" => client_id, "token" => token, "client_type" => client_type })
|
||||
client.onopenblock.call handshake
|
||||
|
||||
# create a login message, and pass it into the router via onmsgblock.call
|
||||
login = message_factory.login_with_user_pass(user.email, password, :client_id => client_id, :client_type => 'client')
|
||||
|
||||
# first log in
|
||||
client.onmsgblock.call login.to_s
|
||||
|
||||
client
|
||||
end
|
||||
|
||||
|
|
@ -98,15 +92,9 @@ def login_latency_tester(router, latency_tester, client_id)
|
|||
|
||||
@router.new_client(client, true)
|
||||
handshake = double("handshake")
|
||||
handshake.should_receive(:query).twice.and_return({ "pb" => "true", "channel_id" => SecureRandom.uuid })
|
||||
handshake.should_receive(:query).exactly(3).times.and_return({ "pb" => "true", "channel_id" => SecureRandom.uuid, "client_type" => "latency_tester", "client_id" => client_id })
|
||||
client.onopenblock.call handshake
|
||||
|
||||
# create a login message, and pass it into the router via onmsgblock.call
|
||||
login = message_factory.login_with_client_id(client_id)
|
||||
|
||||
# first log in
|
||||
client.onmsgblock.call login.to_s
|
||||
|
||||
client
|
||||
end
|
||||
|
||||
|
|
@ -239,7 +227,7 @@ describe Router do
|
|||
it "should allow login of valid user", :mq => true do
|
||||
@user = FactoryGirl.create(:user,
|
||||
:password => "foobar", :password_confirmation => "foobar")
|
||||
client1 = login(@router, @user, "foobar", "1")
|
||||
client1 = login(@router, @user, "foobar", "1", @user.remember_token, "client")
|
||||
done
|
||||
end
|
||||
|
||||
|
|
@ -271,7 +259,7 @@ describe Router do
|
|||
# make a music_session and define two members
|
||||
|
||||
# create client 1, log him in, and log him in to music session
|
||||
client1 = login(@router, user1, "foobar", "1")
|
||||
client1 = login(@router, user1, "foobar", "1", user1.remember_token, "client")
|
||||
done
|
||||
end
|
||||
|
||||
|
|
@ -284,9 +272,9 @@ describe Router do
|
|||
|
||||
|
||||
# create client 1, log him in, and log him in to music session
|
||||
client1 = login(@router, user1, "foobar", "1")
|
||||
client1 = login(@router, user1, "foobar", "1", user1.remember_token, "client")
|
||||
|
||||
client2 = login(@router, user2, "foobar", "2")
|
||||
client2 = login(@router, user2, "foobar", "2", user2.remember_token, "client")
|
||||
|
||||
# make a music_session and define two members
|
||||
|
||||
|
|
@ -305,10 +293,10 @@ describe Router do
|
|||
music_session = FactoryGirl.create(:active_music_session, :creator => user1)
|
||||
|
||||
# create client 1, log him in, and log him in to music session
|
||||
client1 = login(@router, user1, "foobar", "1")
|
||||
client1 = login(@router, user1, "foobar", "1", user1.remember_token, "client")
|
||||
#login_music_session(@router, client1, music_session)
|
||||
|
||||
client2 = login(@router, user2, "foobar", "2")
|
||||
client2 = login(@router, user2, "foobar", "2", user2.remember_token, "client")
|
||||
#login_music_session(@router, client2, music_session)
|
||||
|
||||
# by creating
|
||||
|
|
|
|||
Loading…
Reference in New Issue