websocket.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. r"""
  2. This module implements the core RPC and publish APIs. Developers can extend
  3. LinkProtocol to provide additional RPC callbacks for their web-applications. Then extend
  4. ServerProtocol to hook all the needed LinkProtocols together.
  5. """
  6. import logging
  7. from . import register as exportRpc
  8. from . import schedule_callback
  9. # =============================================================================
  10. #
  11. # Base class for objects that can accept RPC calls or publish over wslink
  12. #
  13. # =============================================================================
  14. class LinkProtocol(object):
  15. """
  16. Subclass this to communicate with wslink clients. LinkProtocol
  17. objects provide rpc and pub/sub actions.
  18. """
  19. def __init__(self):
  20. # need a no-op in case they are called before connect.
  21. self.publish = lambda x, y: None
  22. self.addAttachment = lambda x: None
  23. self.coreServer = None
  24. def init(self, publish, addAttachment, stopServer):
  25. self.publish = publish
  26. self.addAttachment = addAttachment
  27. self.stopServer = stopServer
  28. def getSharedObject(self, key):
  29. if self.coreServer:
  30. return self.coreServer.getSharedObject(key)
  31. return None
  32. def onConnect(self, request, client_id):
  33. """Called when a new websocket connection is established.
  34. request is the HTTP request header, and client_id an opaque string that
  35. identifies the connection. The default implementation is a noop. A
  36. subclass may redefine it.
  37. """
  38. pass
  39. def onClose(self, client_id):
  40. """Called when a websocket connection is closed."""
  41. pass
  42. # =============================================================================
  43. #
  44. # Base class for wslink ServerProtocol objects
  45. #
  46. # =============================================================================
  47. class ServerProtocol(object):
  48. """
  49. Defines the core server protocol for wslink. Gathers a list of LinkProtocol
  50. objects that provide rpc and publish functionality.
  51. """
  52. def __init__(self):
  53. self.linkProtocols = []
  54. self.secret = None
  55. self.initialize()
  56. def init(self, publish, addAttachment, stopServer):
  57. self.publish = publish
  58. self.addAttachment = addAttachment
  59. self.stopServer = stopServer
  60. def initialize(self):
  61. """
  62. Let sub classes define what they need to do to properly initialize
  63. themselves.
  64. """
  65. pass
  66. def setSharedObject(self, key, shared):
  67. if not hasattr(self, "sharedObjects"):
  68. self.sharedObjects = {}
  69. if shared == None and key in self.sharedObjects:
  70. del self.sharedObjects[key]
  71. else:
  72. self.sharedObjects[key] = shared
  73. def getSharedObject(self, key):
  74. if key in self.sharedObjects:
  75. return self.sharedObjects[key]
  76. else:
  77. return None
  78. def registerLinkProtocol(self, protocol):
  79. assert isinstance(protocol, LinkProtocol)
  80. protocol.coreServer = self
  81. self.linkProtocols.append(protocol)
  82. # Note: this can only be used _before_ a connection is made -
  83. # otherwise the WslinkWebSocketServerProtocol will already have stored references to
  84. # the RPC methods in the protocol.
  85. def unregisterLinkProtocol(self, protocol):
  86. assert isinstance(protocol, LinkProtocol)
  87. protocol.coreServer = None
  88. try:
  89. self.linkProtocols.remove(protocol)
  90. except ValueError as e:
  91. logging.error("Link protocol missing from registered list.")
  92. def getLinkProtocols(self):
  93. return self.linkProtocols
  94. def updateSecret(self, newSecret):
  95. self.secret = newSecret
  96. def onConnect(self, request, client_id):
  97. """Called when a new websocket connection is established.
  98. request is the HTTP request header, and client_id an opaque string that
  99. identifies the connection. The default implementation is a noop. A
  100. subclass may redefine it.
  101. """
  102. pass
  103. def onClose(self, client_id):
  104. """Called when a websocket connection is closed."""
  105. pass
  106. @exportRpc("application.exit")
  107. def exit(self):
  108. """RPC callback to exit"""
  109. self.stopServer()
  110. @exportRpc("application.exit.later")
  111. def exitLater(self, secondsLater=60):
  112. """RPC callback to exit after a short delay"""
  113. print(f"schedule exit for {secondsLater} seconds from now")
  114. schedule_callback(secondsLater, self.stopServer)