Skip to content

Commit

Permalink
#2351: call 'is_needed' on the mixin class to decide if we add it to …
Browse files Browse the repository at this point in the history
…the client connection class

git-svn-id: https://xpra.org/svn/Xpra/trunk@24907 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Jan 5, 2020
1 parent 52a7dc3 commit 6ce211b
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 115 deletions.
6 changes: 3 additions & 3 deletions src/xpra/server/server_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,9 +396,9 @@ def drop_client(reason="unknown", *args):
send_ui = ui_client and not is_request
self.idle_add(self._process_hello_ui, ss, c, auth_caps, send_ui, share_count)

def get_client_connection_class(self, _caps):
from xpra.server.source.client_connection import ClientConnection
return ClientConnection
def get_client_connection_class(self, caps):
from xpra.server.source.client_connection_factory import get_client_connection_class
return get_client_connection_class(caps)


def _process_hello_ui(self, ss, c, auth_caps, send_ui, share_count):
Expand Down
120 changes: 9 additions & 111 deletions src/xpra/server/source/client_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,65 +13,16 @@

from xpra.make_thread import start_thread
from xpra.os_util import monotonic_time
from xpra.util import merge_dicts, notypedict, envbool, envint, typedict, AtomicInteger
from xpra.util import notypedict, envbool, envint, typedict, AtomicInteger
from xpra.net.compression import compressed_wrapper, Compressed
from xpra.server.source.source_stats import GlobalPerformanceStatistics
from xpra.server.source.clientinfo_mixin import ClientInfoMixin
from xpra.server import server_features
from xpra.server.source.stub_source_mixin import StubSourceMixin
from xpra.log import Logger

CC_BASES = [ClientInfoMixin]
#TODO: notifications mixin
if server_features.clipboard:
from xpra.server.source.clipboard_connection import ClipboardConnection
CC_BASES.append(ClipboardConnection)
if server_features.audio:
from xpra.server.source.audio_mixin import AudioMixin
CC_BASES.append(AudioMixin)
if server_features.webcam:
from xpra.server.source.webcam_mixin import WebcamMixin
CC_BASES.append(WebcamMixin)
if server_features.fileprint:
from xpra.server.source.fileprint_mixin import FilePrintMixin
CC_BASES.append(FilePrintMixin)
if server_features.mmap:
from xpra.server.source.mmap_connection import MMAP_Connection
CC_BASES.append(MMAP_Connection)
if server_features.input_devices:
from xpra.server.source.input_mixin import InputMixin
CC_BASES.append(InputMixin)
if server_features.dbus:
from xpra.server.source.dbus_mixin import DBUS_Mixin
CC_BASES.append(DBUS_Mixin)
if server_features.network_state:
from xpra.server.source.networkstate_mixin import NetworkStateMixin
CC_BASES.append(NetworkStateMixin)
if server_features.display:
from xpra.server.source.clientdisplay_mixin import ClientDisplayMixin
CC_BASES.append(ClientDisplayMixin)
if server_features.windows:
from xpra.server.source.windows_mixin import WindowsMixin
CC_BASES.append(WindowsMixin)
#must be after windows mixin so it can assume "self.send_windows" is set
if server_features.encoding:
from xpra.server.source.encodings_mixin import EncodingsMixin
CC_BASES.append(EncodingsMixin)
if server_features.audio and server_features.av_sync:
from xpra.server.source.avsync_mixin import AVSyncMixin
CC_BASES.append(AVSyncMixin)
from xpra.server.source.idle_mixin import IdleMixin
CC_BASES.append(IdleMixin)
CC_BASES = tuple(CC_BASES)
ClientConnectionClass = type('ClientConnectionClass', CC_BASES, {})

log = Logger("server")
elog = Logger("encoding")
proxylog = Logger("proxy")
notifylog = Logger("notify")
bandwidthlog = Logger("bandwidth")

log("ClientConnectionClass%s", CC_BASES)


BANDWIDTH_DETECTION = envbool("XPRA_BANDWIDTH_DETECTION", True)
MIN_BANDWIDTH = envint("XPRA_MIN_BANDWIDTH", 5*1024*1024)
Expand Down Expand Up @@ -100,10 +51,9 @@
and added to the damage_packet_queue.
"""

class ClientConnection(ClientConnectionClass):
class ClientConnection(StubSourceMixin):

def __init__(self, protocol, disconnect_cb, session_name, server,
idle_add, timeout_add, source_remove,
def __init__(self, protocol, disconnect_cb, session_name,
setting_changed,
socket_dir, unix_socket_paths, log_disconnect, bandwidth_limit, bandwidth_detection,
):
Expand All @@ -115,16 +65,6 @@ def __init__(self, protocol, disconnect_cb, session_name, server,
self.disconnect = disconnect_cb
self.session_name = session_name

for bc in CC_BASES:
try:
bc.__init__(self)
bc.init_from(self, protocol, server)
except Exception as e:
raise Exception("failed to initialize %s: %s" % (bc, e)) from None

for c in CC_BASES:
c.init_state(self)

#holds actual packets ready for sending (already encoded)
#these packets are picked off by the "protocol" via 'next_packet()'
#format: packet, wid, pixels, start_send_cb, end_send_cb
Expand All @@ -141,9 +81,6 @@ def __init__(self, protocol, disconnect_cb, session_name, server,
self.socket_dir = socket_dir
self.unix_socket_paths = unix_socket_paths
self.log_disconnect = log_disconnect
self.idle_add = idle_add
self.timeout_add = timeout_add
self.source_remove = source_remove

self.setting_changed = setting_changed
# network constraints:
Expand All @@ -153,17 +90,15 @@ def __init__(self, protocol, disconnect_cb, session_name, server,
#these statistics are shared by all WindowSource instances:
self.statistics = GlobalPerformanceStatistics()

self.init()

def run(self):
# ready for processing:
self.queue_encode = self.start_queue_encode
protocol.set_packet_source(self.next_packet)

self.protocol.set_packet_source(self.next_packet)

def __repr__(self):
return "%s(%i : %s)" % (type(self).__name__, self.counter, self.protocol)

def init(self):
def init_state(self):
self.hello_sent = False
self.info_namespace = False
self.send_notifications = False
Expand Down Expand Up @@ -191,10 +126,8 @@ def init(self):
def is_closed(self):
return self.close_event.isSet()

def close(self):
def cleanup(self):
log("%s.close()", self)
for c in CC_BASES:
c.cleanup(self)
self.close_event.set()
self.protocol = None

Expand Down Expand Up @@ -254,19 +187,7 @@ def update_bandwidth_limits(self):
ws.bandwidth_limit = max(MIN_BANDWIDTH//10, bandwidth_limit*weight//total_weight)


def parse_hello(self, c):
self.ui_client = c.boolget("ui_client", True)
self.wants_encodings = c.boolget("wants_encodings", self.ui_client)
self.wants_display = c.boolget("wants_display", self.ui_client)
self.wants_events = c.boolget("wants_events", False)
self.wants_aliases = c.boolget("wants_aliases", True)
self.wants_versions = c.boolget("wants_versions", True)
self.wants_features = c.boolget("wants_features", True)
self.wants_default_cursor = c.boolget("wants_default_cursor", False)

for mixin in CC_BASES:
mixin.parse_client_caps(self, c)

def parse_client_caps(self, c):
#general features:
self.info_namespace = c.boolget("info-namespace")
self.send_notifications = c.boolget("notifications")
Expand All @@ -288,14 +209,6 @@ def parse_hello(self, c):
bandwidthlog("server bandwidth-limit=%s, client bandwidth-limit=%s, value=%s, detection=%s",
server_bandwidth_limit, bandwidth_limit, self.bandwidth_limit, self.bandwidth_detection)

cinfo = self.get_connect_info()
for i,ci in enumerate(cinfo):
log.info("%s%s", ["", " "][int(i>0)], ci)
if self.client_proxy:
from xpra.version_util import version_compat_check
msg = version_compat_check(self.proxy_version)
if msg:
proxylog.warn("Warning: proxy version may not be compatible: %s", msg)
if getattr(self, "mmap_size", 0)>0:
log("mmap enabled, ignoring bandwidth-limit")
self.bandwidth_limit = 0
Expand Down Expand Up @@ -430,14 +343,6 @@ def send_async(self, *parts, **kwargs):
self.send(*parts, **kwargs)


def send_hello(self, server_capabilities):
capabilities = server_capabilities.copy()
for bc in CC_BASES:
merge_dicts(capabilities, bc.get_caps(self))
self.send("hello", capabilities)
self.hello_sent = True


######################################################################
# info:
def get_info(self) -> dict:
Expand All @@ -459,13 +364,6 @@ def get_info(self) -> dict:
"connection" : p.get_info(),
})
info.update(self.get_features_info())
for bc in CC_BASES:
try:
merge_dicts(info, bc.get_info(self))
except Exception as e:
log("merge_dicts on %s", bc, exc_info=True)
log.error("Error: cannot add information from %s:", bc)
log.error(" %s", e)
return info

def get_features_info(self) -> dict:
Expand Down
147 changes: 147 additions & 0 deletions src/xpra/server/source/client_connection_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# -*- coding: utf-8 -*-
# This file is part of Xpra.
# Copyright (C) 2010-2020 Antoine Martin <[email protected]>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

from xpra.server import server_features
from xpra.util import merge_dicts
from xpra.log import Logger

log = Logger("server")


def get_client_connection_class(caps):
from xpra.server.source.clientinfo_mixin import ClientInfoMixin
CC = [ClientInfoMixin]
#TODO: notifications mixin
if server_features.clipboard:
from xpra.server.source.clipboard_connection import ClipboardConnection
CC.append(ClipboardConnection)
if server_features.audio:
from xpra.server.source.audio_mixin import AudioMixin
CC.append(AudioMixin)
if server_features.webcam:
from xpra.server.source.webcam_mixin import WebcamMixin
CC.append(WebcamMixin)
if server_features.fileprint:
from xpra.server.source.fileprint_mixin import FilePrintMixin
CC.append(FilePrintMixin)
if server_features.mmap:
from xpra.server.source.mmap_connection import MMAP_Connection
CC.append(MMAP_Connection)
if server_features.input_devices:
from xpra.server.source.input_mixin import InputMixin
CC.append(InputMixin)
if server_features.dbus:
from xpra.server.source.dbus_mixin import DBUS_Mixin
CC.append(DBUS_Mixin)
if server_features.network_state:
from xpra.server.source.networkstate_mixin import NetworkStateMixin
CC.append(NetworkStateMixin)
if server_features.display:
from xpra.server.source.clientdisplay_mixin import ClientDisplayMixin
CC.append(ClientDisplayMixin)
if server_features.windows:
from xpra.server.source.windows_mixin import WindowsMixin
CC.append(WindowsMixin)
#must be after windows mixin so it can assume "self.send_windows" is set
if server_features.encoding:
from xpra.server.source.encodings_mixin import EncodingsMixin
CC.append(EncodingsMixin)
if server_features.audio and server_features.av_sync:
from xpra.server.source.avsync_mixin import AVSyncMixin
CC.append(AVSyncMixin)
from xpra.server.source.idle_mixin import IdleMixin
CC.append(IdleMixin)
CC_BASES = []
for c in CC:
r = c.is_needed(caps)
log("get_client_connection_class(..) %s enabled=%s", c, r)
if r:
CC_BASES.append(c)
from xpra.server.source.client_connection import ClientConnection
CC_BASES = tuple([ClientConnection]+list(CC_BASES))
ClientConnectionClass = type('ClientConnectionClass', CC_BASES, {})
log("ClientConnectionClass%s", CC_BASES)

class ClientConnectionMuxer(ClientConnectionClass):

def __init__(self, protocol, disconnect_cb, session_name, server,
idle_add, timeout_add, source_remove,
*args):
self.idle_add = idle_add
self.timeout_add = timeout_add
self.source_remove = source_remove
for bc in CC_BASES:
try:
if bc==ClientConnection:
initargs = [protocol, disconnect_cb, session_name]+list(args)
else:
initargs = ()
bc.__init__(self, *initargs)
bc.init_from(self, protocol, server)
except Exception as e:
log("%s.__init__(..)", bc, exc_info=True)
raise Exception("failed to initialize %s: %s" % (bc, e)) from None

for c in CC_BASES:
c.init_state(self)
self.run()

def close(self):
log("%s.close()", self)
for bc in reversed(CC_BASES):
log("%s.cleanup()", bc)
try:
bc.cleanup(self)
except Exception as e:
log("%s.cleanup()", bc, exc_info=True)
log.error("Error closing connection")
log.error(" %s", e)
raise Exception("failed to initialize %s: %s" % (bc, e)) from None

def send_hello(self, server_capabilities):
capabilities = server_capabilities.copy()
for bc in CC_BASES:
log("%s.get_caps()", bc)
merge_dicts(capabilities, bc.get_caps(self))
self.send("hello", capabilities)
self.hello_sent = True

def get_info(self) -> dict:
info = {}
for bc in CC_BASES:
log("%s.get_info()", bc)
try:
merge_dicts(info, bc.get_info(self))
except Exception as e:
log("merge_dicts on %s", bc, exc_info=True)
log.error("Error: cannot add information from %s:", bc)
log.error(" %s", e)
return info

def parse_hello(self, c):
self.ui_client = c.boolget("ui_client", True)
self.wants_encodings = c.boolget("wants_encodings", self.ui_client)
self.wants_display = c.boolget("wants_display", self.ui_client)
self.wants_events = c.boolget("wants_events", False)
self.wants_aliases = c.boolget("wants_aliases", True)
self.wants_versions = c.boolget("wants_versions", True)
self.wants_features = c.boolget("wants_features", True)
self.wants_default_cursor = c.boolget("wants_default_cursor", False)
for bc in CC_BASES:
log("%s.parse_client_caps(..)", bc)
bc.parse_client_caps(self, c)
#log client info:
cinfo = self.get_connect_info()
for i,ci in enumerate(cinfo):
log.info("%s%s", ["", " "][int(i>0)], ci)
if self.client_proxy:
from xpra.version_util import version_compat_check
msg = version_compat_check(self.proxy_version)
if msg:
proxylog = Logger("proxy")
proxylog.warn("Warning: proxy version may not be compatible: %s", msg)

return ClientConnectionMuxer
10 changes: 10 additions & 0 deletions src/xpra/server/source/mmap_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,16 @@

class MMAP_Connection(StubSourceMixin):

@classmethod
def is_needed(cls, caps):
#pre 2.3 clients;
if caps.strget("mmap_file"):
return True
v = caps.rawget("mmap")
#we should be receiving a dict with mmap attributes
#(but pre v4 clients also send a boolean telling us if mmap is supported by the platform..)
return isinstance(v, dict)

def __init__(self):
self.supports_mmap = False
self.mmap_filename = None
Expand Down
Loading

0 comments on commit 6ce211b

Please sign in to comment.