proxynt 2.0.58__tar.gz → 2.0.59rc2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. {proxynt-2.0.58 → proxynt-2.0.59rc2}/PKG-INFO +1 -1
  2. {proxynt-2.0.58 → proxynt-2.0.59rc2}/client/tcp_forward_client.py +7 -1
  3. {proxynt-2.0.58 → proxynt-2.0.59rc2}/client/udp_forward_client.py +26 -8
  4. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/nat_serialization_v2.py +5 -6
  5. proxynt-2.0.59rc2/common/pool.py +96 -0
  6. {proxynt-2.0.58 → proxynt-2.0.59rc2}/constant/system_constant.py +1 -1
  7. {proxynt-2.0.58 → proxynt-2.0.59rc2}/proxynt.egg-info/PKG-INFO +1 -1
  8. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/tcp_forward_client.py +11 -12
  9. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/websocket_handler.py +10 -3
  10. proxynt-2.0.58/common/pool.py +0 -203
  11. {proxynt-2.0.58 → proxynt-2.0.59rc2}/LICENSE +0 -0
  12. {proxynt-2.0.58 → proxynt-2.0.59rc2}/MANIFEST.in +0 -0
  13. {proxynt-2.0.58 → proxynt-2.0.59rc2}/__init__.py +0 -0
  14. {proxynt-2.0.58 → proxynt-2.0.59rc2}/client/__init__.py +0 -0
  15. {proxynt-2.0.58 → proxynt-2.0.59rc2}/client/abstract_tunnel.py +0 -0
  16. {proxynt-2.0.58 → proxynt-2.0.59rc2}/client/clear_nonce_task.py +0 -0
  17. {proxynt-2.0.58 → proxynt-2.0.59rc2}/client/heart_beat_task.py +0 -0
  18. {proxynt-2.0.58 → proxynt-2.0.59rc2}/client/kcp_tunnel_impl.py +0 -0
  19. {proxynt-2.0.58 → proxynt-2.0.59rc2}/client/n4_tunnel_manager.py +0 -0
  20. {proxynt-2.0.58 → proxynt-2.0.59rc2}/client/quic_tunnel_impl.py +0 -0
  21. {proxynt-2.0.58 → proxynt-2.0.59rc2}/client/tunnel_protocol.py +0 -0
  22. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/__init__.py +0 -0
  23. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/cert_utils.py +0 -0
  24. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/crypto/__init__.py +0 -0
  25. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/crypto/table.py +0 -0
  26. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/encrypt_utils.py +0 -0
  27. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/kcp.py +0 -0
  28. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/logger_factory.py +0 -0
  29. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/n4_protocol.py +0 -0
  30. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/n4_punch.py +0 -0
  31. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/nat_serialization.py +0 -0
  32. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/nat_serialization_v1.py +0 -0
  33. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/register_append_data.py +0 -0
  34. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/speed_limit.py +0 -0
  35. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/__init__.py +0 -0
  36. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/_abnf.py +0 -0
  37. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/_app.py +0 -0
  38. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/_cookiejar.py +0 -0
  39. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/_core.py +0 -0
  40. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/_dispatcher.py +0 -0
  41. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/_exceptions.py +0 -0
  42. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/_handshake.py +0 -0
  43. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/_http.py +0 -0
  44. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/_logging.py +0 -0
  45. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/_socket.py +0 -0
  46. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/_ssl_compat.py +0 -0
  47. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/_url.py +0 -0
  48. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/_utils.py +0 -0
  49. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/_wsdump.py +0 -0
  50. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/tests/__init__.py +0 -0
  51. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/tests/echo-server.py +0 -0
  52. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/tests/test_abnf.py +0 -0
  53. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/tests/test_app.py +0 -0
  54. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/tests/test_cookiejar.py +0 -0
  55. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/tests/test_dispatcher.py +0 -0
  56. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/tests/test_handshake_large_response.py +0 -0
  57. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/tests/test_http.py +0 -0
  58. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/tests/test_large_payloads.py +0 -0
  59. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/tests/test_logging_helpers.py +0 -0
  60. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/tests/test_reconnect_bad_fd.py +0 -0
  61. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/tests/test_socket.py +0 -0
  62. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/tests/test_socket_bugs.py +0 -0
  63. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/tests/test_ssl_compat.py +0 -0
  64. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/tests/test_ssl_edge_cases.py +0 -0
  65. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/tests/test_url.py +0 -0
  66. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/tests/test_utils.py +0 -0
  67. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket/tests/test_websocket.py +0 -0
  68. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/__init__.py +0 -0
  69. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/compliance/autobahn-test-report-Feb-03-2021.html +0 -0
  70. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/setup.py +0 -0
  71. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/__init__.py +0 -0
  72. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/_abnf.py +0 -0
  73. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/_app.py +0 -0
  74. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/_cookiejar.py +0 -0
  75. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/_core.py +0 -0
  76. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/_dispatcher.py +0 -0
  77. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/_exceptions.py +0 -0
  78. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/_handshake.py +0 -0
  79. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/_http.py +0 -0
  80. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/_logging.py +0 -0
  81. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/_socket.py +0 -0
  82. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/_ssl_compat.py +0 -0
  83. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/_url.py +0 -0
  84. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/_utils.py +0 -0
  85. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/_wsdump.py +0 -0
  86. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/tests/__init__.py +0 -0
  87. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/tests/echo-server.py +0 -0
  88. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/tests/test_abnf.py +0 -0
  89. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/tests/test_app.py +0 -0
  90. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/tests/test_cookiejar.py +0 -0
  91. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/tests/test_dispatcher.py +0 -0
  92. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/tests/test_handshake_large_response.py +0 -0
  93. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/tests/test_http.py +0 -0
  94. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/tests/test_large_payloads.py +0 -0
  95. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/tests/test_logging_helpers.py +0 -0
  96. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/tests/test_reconnect_bad_fd.py +0 -0
  97. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/tests/test_socket.py +0 -0
  98. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/tests/test_socket_bugs.py +0 -0
  99. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/tests/test_ssl_compat.py +0 -0
  100. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/tests/test_ssl_edge_cases.py +0 -0
  101. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/tests/test_url.py +0 -0
  102. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/tests/test_utils.py +0 -0
  103. {proxynt-2.0.58 → proxynt-2.0.59rc2}/common/websocket2/websocket/tests/test_websocket.py +0 -0
  104. {proxynt-2.0.58 → proxynt-2.0.59rc2}/constant/__init__.py +0 -0
  105. {proxynt-2.0.58 → proxynt-2.0.59rc2}/constant/message_type_constnat.py +0 -0
  106. {proxynt-2.0.58 → proxynt-2.0.59rc2}/context/__init__.py +0 -0
  107. {proxynt-2.0.58 → proxynt-2.0.59rc2}/context/context_utils.py +0 -0
  108. {proxynt-2.0.58 → proxynt-2.0.59rc2}/entity/__init__.py +0 -0
  109. {proxynt-2.0.58 → proxynt-2.0.59rc2}/entity/client_config_entity.py +0 -0
  110. {proxynt-2.0.58 → proxynt-2.0.59rc2}/entity/message/__init__.py +0 -0
  111. {proxynt-2.0.58 → proxynt-2.0.59rc2}/entity/message/message_entity.py +0 -0
  112. {proxynt-2.0.58 → proxynt-2.0.59rc2}/entity/message/push_config_entity.py +0 -0
  113. {proxynt-2.0.58 → proxynt-2.0.59rc2}/entity/message/tcp_over_websocket_message.py +0 -0
  114. {proxynt-2.0.58 → proxynt-2.0.59rc2}/entity/server_config_entity.py +0 -0
  115. {proxynt-2.0.58 → proxynt-2.0.59rc2}/exceptions/__init__.py +0 -0
  116. {proxynt-2.0.58 → proxynt-2.0.59rc2}/exceptions/duplicated_name.py +0 -0
  117. {proxynt-2.0.58 → proxynt-2.0.59rc2}/exceptions/invalid_password.py +0 -0
  118. {proxynt-2.0.58 → proxynt-2.0.59rc2}/exceptions/replay_error.py +0 -0
  119. {proxynt-2.0.58 → proxynt-2.0.59rc2}/exceptions/signature_error.py +0 -0
  120. {proxynt-2.0.58 → proxynt-2.0.59rc2}/p2ptest/__init__.py +0 -0
  121. {proxynt-2.0.58 → proxynt-2.0.59rc2}/p2ptest/client.py +0 -0
  122. {proxynt-2.0.58 → proxynt-2.0.59rc2}/p2ptest/n4.py +0 -0
  123. {proxynt-2.0.58 → proxynt-2.0.59rc2}/proxynt.egg-info/SOURCES.txt +0 -0
  124. {proxynt-2.0.58 → proxynt-2.0.59rc2}/proxynt.egg-info/dependency_links.txt +0 -0
  125. {proxynt-2.0.58 → proxynt-2.0.59rc2}/proxynt.egg-info/entry_points.txt +0 -0
  126. {proxynt-2.0.58 → proxynt-2.0.59rc2}/proxynt.egg-info/requires.txt +0 -0
  127. {proxynt-2.0.58 → proxynt-2.0.59rc2}/proxynt.egg-info/top_level.txt +0 -0
  128. {proxynt-2.0.58 → proxynt-2.0.59rc2}/run_client.py +0 -0
  129. {proxynt-2.0.58 → proxynt-2.0.59rc2}/run_server.py +0 -0
  130. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/__init__.py +0 -0
  131. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/admin_http_handler.py +0 -0
  132. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/n4.py +0 -0
  133. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/n4_signal_service.py +0 -0
  134. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/task/__init__.py +0 -0
  135. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/task/check_cookie_task.py +0 -0
  136. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/task/clear_nonce_task.py +0 -0
  137. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/task/heart_beat_task.py +0 -0
  138. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/template/__init__.py +0 -0
  139. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/template/base.html +0 -0
  140. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/template/css/fonts/element-icons.woff +0 -0
  141. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/template/css/index.css +0 -0
  142. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/template/ele_index.html +0 -0
  143. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/template/js/axios.min.js +0 -0
  144. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/template/js/index.js +0 -0
  145. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/template/js/vue.min.js +0 -0
  146. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/template/login.html +0 -0
  147. {proxynt-2.0.58 → proxynt-2.0.59rc2}/server/udp_forward_client.py +0 -0
  148. {proxynt-2.0.58 → proxynt-2.0.59rc2}/setup.cfg +0 -0
  149. {proxynt-2.0.58 → proxynt-2.0.59rc2}/setup.py +0 -0
  150. {proxynt-2.0.58 → proxynt-2.0.59rc2}/test_exchange.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: proxynt
3
- Version: 2.0.58
3
+ Version: 2.0.59rc2
4
4
  Summary: UNKNOWN
5
5
  Home-page: https://github.com/sazima/proxynt
6
6
  License: UNKNOWN
@@ -176,7 +176,7 @@ class TcpForwardClient:
176
176
  if data.speed_limiter and recv:
177
177
  wait_time = data.speed_limiter.acquire(len(recv))
178
178
  if wait_time > 0:
179
- time.sleep(wait_time)
179
+ self.socket_event_loop.pause_and_resume_later(each, wait_time)
180
180
 
181
181
  if self.tunnel_manager:
182
182
  if self.tunnel_manager.send_data(connection.uid, recv):
@@ -366,9 +366,15 @@ class TcpForwardClient:
366
366
  return
367
367
  try:
368
368
  s = connection.socket
369
+ s.settimeout(30)
369
370
  s.sendall(msg)
371
+ s.settimeout(None)
370
372
  if not msg:
371
373
  self.close_connection(s)
374
+ except socket.timeout:
375
+ LoggerFactory.get_logger().warning('sendall timeout, closing uid: %s' % connection.uid.hex())
376
+ self.close_connection(s)
377
+ self.close_remote_socket(connection)
372
378
  except Exception:
373
379
  LoggerFactory.get_logger().error(traceback.format_exc())
374
380
  self.close_remote_socket(connection)
@@ -188,24 +188,42 @@ class UdpForwardClient:
188
188
  LoggerFactory.get_logger().error(traceback.format_exc())
189
189
 
190
190
  def _udp_receive_loop(self):
191
+ import select as _select
191
192
  LoggerFactory.get_logger().info('UDP receive thread started')
192
193
  while self.running:
193
194
  connections = list(self.uid_to_connection.values())
195
+ if not connections:
196
+ time.sleep(0.1)
197
+ continue
198
+ sock_to_conn = {}
194
199
  for conn in connections:
195
200
  try:
196
201
  conn.socket.setblocking(False)
197
- try:
198
- data, addr = conn.socket.recvfrom(65536)
199
- if data:
200
- self._handle_udp_data(conn, data, addr)
201
- except (BlockingIOError, socket.error):
202
- pass
202
+ sock_to_conn[conn.socket] = conn
203
+ except Exception:
204
+ pass
205
+ if not sock_to_conn:
206
+ time.sleep(0.1)
207
+ continue
208
+ try:
209
+ readable, _, _ = _select.select(list(sock_to_conn.keys()), [], [], 0.5)
210
+ except (OSError, ValueError):
211
+ time.sleep(0.1)
212
+ continue
213
+ for sock in readable:
214
+ conn = sock_to_conn.get(sock)
215
+ if not conn:
216
+ continue
217
+ try:
218
+ data, addr = sock.recvfrom(65536)
219
+ if data:
220
+ self._handle_udp_data(conn, data, addr)
221
+ except (BlockingIOError, socket.error):
222
+ pass
203
223
  except Exception as e:
204
224
  LoggerFactory.get_logger().error('UDP data receive error: %s' % e)
205
225
  LoggerFactory.get_logger().error(traceback.format_exc())
206
226
 
207
- time.sleep(0.001)
208
-
209
227
  def _handle_udp_data(self, conn: UdpSocketConnection, data: bytes, addr):
210
228
  if conn.speed_limiter and data:
211
229
  wait_time = conn.speed_limiter.acquire(len(data))
@@ -42,12 +42,11 @@ class NatSerializationV2:
42
42
 
43
43
  # 处理 data_content,确保可以被 msgpack 序列化
44
44
  if data_content is not None:
45
- # 复制一份避免修改原数据
46
- if compress:
47
- serializable_content = dict(data_content) if isinstance(data_content, dict) else data_content
48
- else:
49
- serializable_content = data_content
50
- # 如果有 'data' 字段且需要压缩
45
+ serializable_content = data_content
46
+ # 如果有 'data' 字段且需要压缩,才复制一份避免修改原数据
47
+ if (compress and has_snappy and isinstance(data_content, dict)
48
+ and 'data' in data_content and data_content['data']):
49
+ serializable_content = dict(data_content)
51
50
  if isinstance(serializable_content, dict) and 'data' in serializable_content:
52
51
  if compress and has_snappy and serializable_content['data']:
53
52
  original_size = len(serializable_content['data'])
@@ -0,0 +1,96 @@
1
+ import logging
2
+ import socket
3
+ import threading
4
+ import time
5
+ import traceback
6
+ from selectors import DefaultSelector, EVENT_READ
7
+
8
+ from common.logger_factory import LoggerFactory
9
+ from common.register_append_data import ResisterAppendData
10
+ from constant.system_constant import SystemConstant
11
+
12
+ """
13
+ 单线程 selector 事件循环, 使用 modify 切换事件掩码
14
+ 参考 shadowsocks eventloop 模式
15
+ """
16
+
17
+
18
+ class SelectPool:
19
+
20
+ def __init__(self):
21
+ self.is_running = True
22
+ self.fileno_to_client = dict()
23
+ self.selector = DefaultSelector()
24
+
25
+ def stop(self):
26
+ self.is_running = False
27
+
28
+ def clear(self):
29
+ self.fileno_to_client.clear()
30
+
31
+ def register(self, s: socket.socket, data: ResisterAppendData):
32
+ self.fileno_to_client[s.fileno()] = s
33
+ self.selector.register(s, EVENT_READ, data)
34
+
35
+ def unregister(self, s: socket.socket):
36
+ fileno = -1
37
+ try:
38
+ fileno = s.fileno()
39
+ except Exception:
40
+ pass
41
+ if fileno in self.fileno_to_client:
42
+ self.fileno_to_client.pop(fileno)
43
+ try:
44
+ self.selector.unregister(s)
45
+ except (KeyError, ValueError):
46
+ pass
47
+ except OSError:
48
+ LoggerFactory.get_logger().error(traceback.format_exc())
49
+
50
+ def pause_reading(self, s: socket.socket):
51
+ """暂停监听可读事件 (modify 掩码为 0)"""
52
+ try:
53
+ key = self.selector.get_key(s)
54
+ self.selector.modify(s, 0, key.data)
55
+ except (KeyError, ValueError):
56
+ pass
57
+
58
+ def resume_reading(self, s: socket.socket):
59
+ """恢复监听可读事件"""
60
+ try:
61
+ key = self.selector.get_key(s)
62
+ self.selector.modify(s, EVENT_READ, key.data)
63
+ except (KeyError, ValueError):
64
+ pass
65
+
66
+ def pause_and_resume_later(self, s: socket.socket, delay_time: float):
67
+ """暂停可读监听, delay_time 秒后恢复"""
68
+ self.pause_reading(s)
69
+ threading.Timer(delay_time, self.resume_reading, args=(s,)).start()
70
+
71
+ def run(self):
72
+ while True:
73
+ if not self.is_running:
74
+ time.sleep(1)
75
+ continue
76
+ try:
77
+ try:
78
+ ready = self.selector.select(timeout=SystemConstant.DEFAULT_TIMEOUT)
79
+ except OSError:
80
+ time.sleep(0.5)
81
+ continue
82
+ for key, mask in ready:
83
+ fileno = key.fd
84
+ client = self.fileno_to_client.get(fileno)
85
+ if client is None:
86
+ if LoggerFactory.get_logger().isEnabledFor(logging.DEBUG):
87
+ LoggerFactory.get_logger().debug('fd %s not in fileno_to_client' % fileno)
88
+ continue
89
+ data: ResisterAppendData = key.data
90
+ try:
91
+ data.callable_(client, data)
92
+ except Exception:
93
+ LoggerFactory.get_logger().error(traceback.format_exc())
94
+ except Exception:
95
+ LoggerFactory.get_logger().error(traceback.format_exc())
96
+ time.sleep(1)
@@ -15,7 +15,7 @@ class SystemConstant:
15
15
 
16
16
  COOKIE_EXPIRE_SECONDS = 3600 * 24
17
17
 
18
- VERSION = '2.0.58'
18
+ VERSION = '2.0.59.rc2'
19
19
 
20
20
  GITHUB = 'https://github.com/sazima/proxynt'
21
21
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: proxynt
3
- Version: 2.0.58
3
+ Version: 2.0.59rc2
4
4
  Summary: UNKNOWN
5
5
  Home-page: https://github.com/sazima/proxynt
6
6
  License: UNKNOWN
@@ -111,13 +111,13 @@ class TcpForwardClient:
111
111
  server_socket_list.append(server)
112
112
  for c in client_connection_list:
113
113
  c.socket.close()
114
- await self.socket_event_loop.async_unregister(c.socket)
114
+ self.socket_event_loop.unregister(c.socket)
115
115
  self.socket_to_connection.pop(c.socket)
116
116
  self.uid_to_connection.pop(c.uid)
117
117
  for s in server_socket_list:
118
118
  self.listen_socket_to_public_server.pop(s.socket_server)
119
119
  try:
120
- await self.socket_event_loop.async_unregister(s.socket_server)
120
+ self.socket_event_loop.unregister(s.socket_server)
121
121
  s.socket_server.shutdown(socket.SHUT_RDWR)
122
122
  except OSError:
123
123
  pass
@@ -164,11 +164,11 @@ class TcpForwardClient:
164
164
  LoggerFactory.get_logger().error(f'Close error: {traceback.format_exc()}')
165
165
  return
166
166
 
167
- # --- 发送端限速:在发送前等待 ---
167
+ # --- 发送端限速:暂停读取,延迟恢复 ---
168
168
  if data.speed_limiter and recv:
169
169
  wait_time = data.speed_limiter.acquire(len(recv))
170
170
  if wait_time > 0:
171
- time.sleep(wait_time)
171
+ self.socket_event_loop.pause_and_resume_later(each, wait_time)
172
172
 
173
173
  if LoggerFactory.get_logger().isEnabledFor(logging.DEBUG):
174
174
  LoggerFactory.get_logger().debug(f'send to ws uid: {socket_connection.uid}, len: {len(recv)}')
@@ -235,9 +235,8 @@ class TcpForwardClient:
235
235
  if uid not in self.uid_to_connection:
236
236
  LoggerFactory.get_logger().debug(f'{message}, {uid} not in ')
237
237
  return
238
- if uid not in self.uid_to_send_lock:
239
- self.uid_to_send_lock[uid] = AsyncioLock()
240
- async with self.uid_to_send_lock[uid]:
238
+ lock = self.uid_to_send_lock.setdefault(uid, AsyncioLock())
239
+ async with lock:
241
240
  connection = self.uid_to_connection.get(uid)
242
241
  if not connection:
243
242
  return
@@ -273,7 +272,7 @@ class TcpForwardClient:
273
272
  connection.socket_server.delete_client(connection)
274
273
  # Ensure unregister before closing
275
274
  try:
276
- await self.socket_event_loop.async_unregister(connection.socket)
275
+ self.socket_event_loop.unregister(connection.socket)
277
276
  except Exception as e:
278
277
  LoggerFactory.get_logger().error(f'Error unregistering socket: {e}')
279
278
 
@@ -288,13 +287,13 @@ class TcpForwardClient:
288
287
  def close_connection(self, connection: PublicSocketConnection):
289
288
  try:
290
289
  LoggerFactory.get_logger().info(f'Closing connection {connection.uid}')
291
- # with self.close_lock:
292
290
  uid = connection.uid
293
291
  if uid not in self.uid_to_connection:
294
292
  return
295
293
  self.socket_event_loop.unregister(connection.socket)
296
- self.uid_to_connection.pop(uid)
297
- self.socket_to_connection.pop(connection.socket)
294
+ self.uid_to_connection.pop(uid, None)
295
+ self.socket_to_connection.pop(connection.socket, None)
296
+ self.uid_to_send_lock.pop(uid, None)
298
297
  connection.socket_server.delete_client(connection)
299
298
  connection.socket.close()
300
299
  except Exception:
@@ -306,7 +305,7 @@ class TcpForwardClient:
306
305
  s: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
307
306
  s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
308
307
  s.bind(('', port))
309
- s.listen(5)
308
+ s.listen(256)
310
309
  return s
311
310
 
312
311
  def close(self):
@@ -4,7 +4,6 @@ import logging
4
4
  import time
5
5
  import traceback
6
6
  from asyncio import Lock
7
- from concurrent.futures import ThreadPoolExecutor
8
7
  from json import JSONDecodeError
9
8
  from typing import List, Dict, Set, Tuple
10
9
 
@@ -31,7 +30,7 @@ from exceptions.replay_error import ReplayError
31
30
  from exceptions.signature_error import SignatureError
32
31
  from server.tcp_forward_client import TcpForwardClient
33
32
 
34
- p = ThreadPoolExecutor(max_workers=100)
33
+ MAX_CONCURRENT_MESSAGES = 64
35
34
 
36
35
 
37
36
  class MyWebSocketaHandler(WebSocketHandler):
@@ -63,6 +62,7 @@ class MyWebSocketaHandler(WebSocketHandler):
63
62
 
64
63
  def open(self, *args: str, **kwargs: str):
65
64
  self.lock = Lock()
65
+ self._msg_sem = asyncio.Semaphore(MAX_CONCURRENT_MESSAGES)
66
66
  self.client_name = None
67
67
  self.version = None
68
68
 
@@ -115,7 +115,14 @@ class MyWebSocketaHandler(WebSocketHandler):
115
115
  raise
116
116
 
117
117
  def on_message(self, m_bytes):
118
- asyncio.ensure_future(self.on_message_async(m_bytes))
118
+ asyncio.ensure_future(self._on_message_with_backpressure(m_bytes))
119
+
120
+ async def _on_message_with_backpressure(self, m_bytes):
121
+ await self._msg_sem.acquire()
122
+ try:
123
+ await self.on_message_async(m_bytes)
124
+ finally:
125
+ self._msg_sem.release()
119
126
 
120
127
  async def on_message_async(self, message):
121
128
  tcp_forward_client = TcpForwardClient.get_instance()
@@ -1,203 +0,0 @@
1
- import asyncio
2
- import logging
3
- import threading
4
- import time
5
- import weakref
6
- from concurrent.futures import ThreadPoolExecutor
7
- from selectors import DefaultSelector, EVENT_READ
8
-
9
- import socket
10
- import traceback
11
- from typing import Dict, List, Set
12
-
13
- from common.logger_factory import LoggerFactory
14
- from common.register_append_data import ResisterAppendData
15
- from constant.system_constant import SystemConstant
16
-
17
- """
18
- 这里只监听了 socket 的可读状态
19
- """
20
- max_workers = 99
21
-
22
-
23
- class SelectPool:
24
-
25
- def __init__(self):
26
- self.is_running = True
27
- self.fileno_to_client: Dict[int, socket.socket] = dict()
28
- self.selector = DefaultSelector()
29
- self.waiting_register_socket: Set = set()
30
-
31
- self.socket_to_register_lock: Dict[socket.socket, threading.Lock] = dict()
32
- self.socket_to_recv_lock: Dict[socket.socket, threading.Lock] = dict()
33
- self.executor = ThreadPoolExecutor(max_workers=max_workers) #
34
-
35
- def stop(self):
36
- self.is_running = False
37
-
38
- def clear(self):
39
- self.fileno_to_client.clear()
40
- self.waiting_register_socket.clear()
41
- self.socket_to_register_lock.clear()
42
- self.socket_to_recv_lock.clear()
43
-
44
- def register(self, s: socket.socket, data: ResisterAppendData):
45
- self.socket_to_register_lock[s] = threading.Lock()
46
- self.socket_to_recv_lock[s] = threading.Lock()
47
- self.fileno_to_client[s.fileno()] = s
48
- self.selector.register(s, EVENT_READ, data)
49
-
50
- def unregister_and_register_delay(self, s: socket.socket, data: ResisterAppendData, delay_time: int):
51
- """取消注册, 并在指定秒后注册"""
52
-
53
- def _register_again():
54
- try:
55
- if s not in self.socket_to_register_lock:
56
- return
57
- is_exceed, remain = data.speed_limiter.is_exceed()
58
- if is_exceed:
59
- # 再次延迟检测
60
- if LoggerFactory.get_logger().isEnabledFor(logging.DEBUG):
61
- LoggerFactory.get_logger().debug('delay register again, maybe next: %.2f seconds " ' % (remain / data.speed_limiter.max_speed))
62
- threading.Timer(delay_time, _register_again).start()
63
- return
64
- with self.socket_to_register_lock[s]:
65
- if s in self.waiting_register_socket: # 在等待列表中
66
- self.waiting_register_socket.remove(s)
67
- self.register(s, data)
68
- except Exception:
69
- LoggerFactory.get_logger().error(traceback.format_exc())
70
- raise
71
-
72
- if s in self.waiting_register_socket: # 不在等待列表中
73
- return
74
- if s not in self.socket_to_register_lock:
75
- return
76
- with self.socket_to_register_lock[s]:
77
- try:
78
- self.selector.unregister(s)
79
- self.waiting_register_socket.add(s)
80
- except OSError:
81
- LoggerFactory.get_logger().error(traceback.format_exc())
82
- threading.Timer(delay_time, _register_again).start()
83
-
84
- async def async_unregister(self, s: socket.socket):
85
- """添加超时机制的异步取消注册"""
86
- try:
87
- await asyncio.wait_for(
88
- asyncio.get_event_loop().run_in_executor(self.executor, self.unregister, s),
89
- timeout=5 # 5秒超时
90
- )
91
- except asyncio.TimeoutError:
92
- LoggerFactory.get_logger().error(f"Timeout unregistering socket {s}")
93
- # 强制从跟踪字典中移除
94
- if s.fileno() in self.fileno_to_client:
95
- self.fileno_to_client.pop(s.fileno())
96
- if s in self.socket_to_register_lock:
97
- self.socket_to_register_lock.pop(s)
98
- if s in self.socket_to_recv_lock:
99
- self.socket_to_recv_lock.pop(s)
100
- if s in self.waiting_register_socket:
101
- self.waiting_register_socket.remove(s)
102
-
103
- def unregister(self, s: socket.socket):
104
- if s not in self.socket_to_register_lock:
105
- LoggerFactory.get_logger().info('not register socket, skip')
106
- return
107
- with self.socket_to_register_lock[s]:
108
- if s in self.waiting_register_socket:
109
- self.waiting_register_socket.remove(s)
110
- if s.fileno() in self.fileno_to_client:
111
- self.fileno_to_client.pop(s.fileno())
112
- try:
113
- self.selector.unregister(s)
114
- except KeyError:
115
- # KeyError 代表已经注销
116
- pass
117
- except ValueError:
118
- # ? value error 代表已经注销?
119
- pass
120
- except OSError:
121
- LoggerFactory.get_logger().error(traceback.format_exc())
122
- if s in self.socket_to_register_lock:
123
- self.socket_to_register_lock.pop(s)
124
- if s in self.socket_to_recv_lock:
125
- self.socket_to_recv_lock.pop(s)
126
-
127
- def run(self):
128
- while True:
129
- if not self.is_running:
130
- time.sleep(1)
131
- continue
132
- try:
133
- try:
134
- ready = self.selector.select(timeout=SystemConstant.DEFAULT_TIMEOUT)
135
- except OSError:
136
- # 监听列表为空的时候, windows会有os error
137
- # LoggerFactory.get_logger().warn
138
- time.sleep(0.5)
139
- continue
140
- for key, mask in ready:
141
- fileno = key.fd
142
- client = self.fileno_to_client.get(fileno)
143
- if client is None:
144
- LoggerFactory.get_logger().warn(f'key error, {fileno}, self.fileno_to_client: {self.fileno_to_client}')
145
- continue
146
- data: ResisterAppendData = key.data
147
- # lock = self.socket_to_recv_lock[client]
148
- # if not lock.acquire(blocking=False):
149
- # if LoggerFactory.get_logger().isEnabledFor(logging.DEBUG):
150
- # LoggerFactory.get_logger().debug(f'lock continue')
151
- # time.sleep(.005)
152
- # continue # 已被其他线程处理,跳过
153
- self.selector.unregister(client) # register 防止一直就绪状态 耗cpu
154
- self.executor.submit(self._handle_client, client, data)
155
- except Exception:
156
- LoggerFactory.get_logger().error(traceback.format_exc())
157
- time.sleep(1)
158
-
159
- def _handle_client(self, client, data):
160
- try:
161
- data.callable_(client, data)
162
- except Exception:
163
- LoggerFactory.get_logger().error(traceback.format_exc())
164
- finally:
165
- lock = self.socket_to_register_lock.get(client)
166
- if lock is not None:
167
- if not lock.acquire(blocking=True): # 正在锁,跳过
168
- return
169
- try:
170
- if client not in self.socket_to_register_lock:
171
- LoggerFactory.get_logger().warning('[POOL] re-register skipped: lock removed, fd=%s' % client.fileno())
172
- return
173
- self.selector.register(client, EVENT_READ, data)
174
- except Exception:
175
- LoggerFactory.get_logger().error('[POOL] re-register failed fd=%s: %s' % (client.fileno(), traceback.format_exc()))
176
- finally:
177
- lock.release()
178
- else:
179
- if LoggerFactory.get_logger().isEnabledFor(logging.DEBUG):
180
- LoggerFactory.get_logger().debug('[POOL] re-register skipped: lock is None, fd=%s' % client.fileno())
181
- # self.
182
- # if client in self.socket_to_recv_lock:
183
- # self.socket_to_recv_lock[client].release()
184
-
185
- # try:
186
- # start = time.time()
187
- # if client in self.socket_to_register_lock:
188
- # with self.socket_to_register_lock[client]:
189
- # try:
190
- # self.selector.register(client, EVENT_READ, data)
191
- # except Exception as e:
192
- # LoggerFactory.get_logger().warning(f'register error {e}')
193
- # else:
194
- # LoggerFactory.get_logger().warning(f'client not in lock')
195
- # if lock.locked():
196
- # lock.release()
197
- # else:
198
- # LoggerFactory.get_logger().warning(f'lock not in lock')
199
- # # if LoggerFactory.get_logger().isEnabledFor(logging.DEBUG):
200
- # # LoggerFactory.get_logger().debug(f'register cost {time.time() - start} seconds')
201
- # except Exception:
202
- # LoggerFactory.get_logger().error(traceback.format_exc())
203
- #
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes