Skip to content

Commit cd243e5

Browse files
committedOct 16, 2023
5.8.1 Improve performance.
1 parent a210426 commit cd243e5

30 files changed

+343
-139
lines changed
 

‎code/default/launcher/web_control.py

-41
Original file line numberDiff line numberDiff line change
@@ -896,47 +896,6 @@ def req_debug_handler(self):
896896
self.send_response("text/plain", dat)
897897

898898
def req_log_files(self):
899-
# collect debug info and save to folders
900-
debug_infos = {
901-
"system_info": "http://localhost:8085/debug",
902-
"xtunnel_info": "http://127.0.0.1:8085/module/x_tunnel/control/debug",
903-
"xtunnel_status": "http://127.0.0.1:8085/module/x_tunnel/control/status",
904-
"cloudflare_info": "http://127.0.0.1:8085/module/x_tunnel/control/cloudflare_front/debug",
905-
"tls_info": "http://127.0.0.1:8085/module/x_tunnel/control/tls_relay_front/debug",
906-
"seley_info": "http://127.0.0.1:8085/module/x_tunnel/control/seley_front/debug",
907-
"cloudflare_log": "http://localhost:8085/module/x_tunnel/control/cloudflare_front/log?cmd=get_new&last_no=1",
908-
"tls_log": "http://localhost:8085/module/x_tunnel/control/tls_relay_front/log?cmd=get_new&last_no=1",
909-
"seley_log": "http://localhost:8085/module/x_tunnel/control/seley_front/log?cmd=get_new&last_no=1",
910-
"xtunnel_log": "http://localhost:8085/module/x_tunnel/control/log?cmd=get_new&last_no=1",
911-
"smartroute_log": "http://localhost:8085/module/smart_router/control/log?cmd=get_new&last_no=1",
912-
"launcher_log": "http://localhost:8085/log?cmd=get_new&last_no=1"
913-
}
914-
915-
download_path = os.path.join(env_info.data_path, "downloads")
916-
if not os.path.isdir(download_path):
917-
os.mkdir(download_path)
918-
919-
for name, url in debug_infos.items():
920-
# xlog.debug("fetch %s %s", name, url)
921-
try:
922-
res = simple_http_client.request("GET", url, timeout=1)
923-
if name.endswith("log"):
924-
dat = json.loads(res.text)
925-
no_line = list(dat.items())
926-
no_line = [[int(line[0]), line[1]] for line in no_line]
927-
no_line = sorted(no_line, key=operator.itemgetter(0))
928-
lines = [line[1] for line in no_line]
929-
data = "".join(lines)
930-
data = utils.to_bytes(data)
931-
else:
932-
data = res.text
933-
934-
fn = os.path.join(download_path, name + ".txt")
935-
with open(fn, "wb") as fd:
936-
fd.write(data)
937-
except Exception as e:
938-
xlog.exception("fetch info %s fail:%r", url, e)
939-
940899
# pack data folder and response
941900
x_tunnel_local = os.path.abspath(os.path.join(default_path, 'x_tunnel', 'local'))
942901
sys.path.append(x_tunnel_local)

‎code/default/lib/noarch/front_base/config.py

+7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ def set_default(self):
2222
self.set_var("dispather_max_idle_workers", 30)
2323
self.set_var("dispather_worker_max_continue_fail", 8)
2424
self.set_var("dispather_connect_all_workers_on_startup", 0)
25+
self.set_var("dispather_ping_check_speed_interval", 300)
26+
self.set_var("dispather_ping_upload_size", 1024)
27+
self.set_var("dispather_ping_download_size", 10240)
2528

2629
self.set_var("max_task_num", 100)
2730

@@ -30,6 +33,7 @@ def set_default(self):
3033
self.set_var("http1_ping_interval", 300)
3134
self.set_var("http1_idle_time", 360)
3235
self.set_var("http1_max_process_tasks", 99999999)
36+
self.set_var("http1_trace_size", 20)
3337

3438
# http 2 worker
3539
self.set_var("http2_max_concurrent", 60)
@@ -92,6 +96,9 @@ def set_default(self):
9296
self.set_var("long_fail_connect_interval", 180)
9397
self.set_var("short_fail_connect_interval", 10)
9498
self.set_var("shuffle_ip_on_first_load", 0)
99+
self.set_var("ip_speed_history_size", 10)
100+
self.set_var("ip_initial_speed", 1000000)
101+
self.set_var("ip_speed_save_interval", 60)
95102

96103
# ip source
97104
self.set_var("use_ipv6", "auto") #force_ipv4/force_ipv6

‎code/default/lib/noarch/front_base/connect_manager.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -243,14 +243,18 @@ def _create_more_connection_worker(self):
243243
self.logger.warning("Connect creating process blocked, max connect thread increase to %d",
244244
self.config.https_max_connect_thread)
245245

246-
while self.thread_num < self.config.https_max_connect_thread and self._need_more_ip():
247-
246+
for i in range(self.thread_num, self.config.https_max_connect_thread):
248247
self.thread_num_lock.acquire()
249248
self.thread_num += 1
250249
self.thread_num_lock.release()
250+
251251
p = threading.Thread(target=self._connect_thread, name="%s_conn_manager__connect_th" % self.logger.name)
252252
p.start()
253-
time.sleep(self.config.connect_create_interval)
253+
if self.config.connect_create_interval > 0.1:
254+
time.sleep(self.config.connect_create_interval)
255+
256+
if not self._need_more_ip():
257+
break
254258

255259
with self.thread_num_lock:
256260
self.connecting_more_thread = None

‎code/default/lib/noarch/front_base/http1.py

+22-5
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ def __init__(self, logger, ip_manager, config, ssl_sock, close_cb, retry_task_cb
4040

4141
def record_active(self, active=""):
4242
self.trace_time.append([time.time(), active])
43+
if len(self.trace_time) > self.config.http1_trace_size:
44+
self.trace_time.pop(0)
4345
# self.logger.debug("%s stat:%s", self.ip, active)
4446

4547
def get_trace(self):
@@ -141,7 +143,8 @@ def request_task(self, task):
141143

142144
task.headers[b'Host'] = self.get_host(task.host)
143145

144-
task.headers[b"Content-Length"] = len(task.body)
146+
request_len = len(task.body)
147+
task.headers[b"Content-Length"] = request_len
145148
request_data = b'%s %s HTTP/1.1\r\n' % (task.method, task.path)
146149
request_data += pack_headers(task.headers)
147150
request_data += b'\r\n'
@@ -184,7 +187,11 @@ def request_task(self, task):
184187
response.worker = self
185188
task.content_length = response.content_length
186189
task.responsed = True
187-
task.queue.put(response)
190+
if task.queue:
191+
task.queue.put(response)
192+
else:
193+
if self.config.http2_show_debug:
194+
self.logger.debug("got pong for %s status:%d", self.ip_str, response.status)
188195

189196
try:
190197
read_target = int(response.content_length)
@@ -219,9 +226,19 @@ def request_task(self, task):
219226
task.finish()
220227

221228
self.ssl_sock.received_size += data_len
222-
time_cost = (time.time() - start_time)
223-
if time_cost != 0:
224-
speed = data_len / time_cost
229+
230+
time_now = time.time()
231+
time_cost = (time_now - start_time)
232+
xcost = float(response.headers.get(b"X-Cost", 0))
233+
if isinstance(xcost, list):
234+
xcost = float(xcost[0])
235+
236+
total_len = (request_len + data_len)
237+
road_time = time_cost - xcost
238+
if xcost and total_len > 10000 and road_time:
239+
speed = total_len / road_time
240+
self.update_speed(speed)
241+
225242
task.set_state("h1_finish[SP:%d]" % speed)
226243

227244
self.transfered_size += len(request_data) + data_len

‎code/default/lib/noarch/front_base/http2_connection.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -462,8 +462,8 @@ def receive_frame(self, frame):
462462
# code registry otherwise use the frame's additional data.
463463
error_string = frame._extra_info()
464464
time_cost = time.time() - self.last_recv_time
465-
if frame.additional_data != b"session_timed_out":
466-
self.logger.warn("goaway:%s, t:%d", error_string, time_cost)
465+
# if frame.additional_data != b"session_timed_out":
466+
# self.logger.warn("goaway:%s, t:%d", error_string, time_cost)
467467

468468
self.close("GoAway:%s inactive time:%d" % (error_string, time_cost))
469469

‎code/default/lib/noarch/front_base/http2_stream.py

+11-6
Original file line numberDiff line numberDiff line change
@@ -316,11 +316,13 @@ def receive_frame(self, frame):
316316

317317
time_now = time.time()
318318
whole_cost = time_now - self.start_time
319+
rtt = whole_cost - xcost
319320
receive_cost = time_now - self.get_head_time
320321
bytes_received = self.connection._sock.bytes_received - self.start_connection_point
321-
if receive_cost > 0 and bytes_received > 10000 and not self.task.finished and receive_cost > 0.001 \
322-
and xcost >= 0:
323-
rtt = whole_cost - xcost
322+
if b"ping" in self.task.path and self.config.http2_show_debug:
323+
self.logger.debug("got pong for %s", self.connection.ip_str)
324+
325+
if rtt > 0 and bytes_received >= 10000 and not self.task.finished and xcost >= 0.0:
324326
t_road = rtt
325327
if t_road <= self.connection.handshake:
326328
# adjust handshake
@@ -367,10 +369,13 @@ def send_response(self):
367369
response.ssl_sock = self.connection.ssl_sock
368370
response.worker = self.connection
369371
response.task = self.task
370-
if self.config.http2_show_debug:
371-
self.logger.debug("self.task.queue.put(response)")
372372

373-
self.task.queue.put(response)
373+
if self.task.queue:
374+
self.task.queue.put(response)
375+
else:
376+
if self.config.http2_show_debug:
377+
self.logger.debug("got pong for %s status:%d", self.connection.ip_str, status)
378+
374379
if status in self.config.http2_status_to_close:
375380
self.connection.close("status %d" % status)
376381

‎code/default/lib/noarch/front_base/http_common.py

+16-29
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ def response_fail(self, reason=""):
177177
res = simple_http_client.BaseResponse(body=err_text)
178178
res.task = self
179179
res.worker = self.worker
180-
self.queue.put(res)
180+
if self.queue:
181+
self.queue.put(res)
181182
self.finish()
182183

183184
def finish(self):
@@ -198,7 +199,6 @@ def __init__(self, logger, ip_manager, config, ssl_sock, close_cb, retry_task_cb
198199
self.ssl_sock = ssl_sock
199200
self.handshake = ssl_sock.handshake_time * 0.001
200201
self.rtt = ssl_sock.handshake_time * 0.001
201-
self.speed = 15000000
202202
self.streams = []
203203
self.ip_str = ssl_sock.ip_str
204204
self.close_cb = close_cb
@@ -213,7 +213,6 @@ def __init__(self, logger, ip_manager, config, ssl_sock, close_cb, retry_task_cb
213213
self.continue_fail_tasks = 0
214214
self.rtt_history = [self.rtt,]
215215
self.adjust_history = []
216-
self.speed_history = [self.speed, self.speed, self.speed]
217216
self.last_recv_time = self.ssl_sock.create_time
218217
self.last_send_time = self.ssl_sock.create_time
219218
self.life_end_time = self.ssl_sock.create_time + \
@@ -228,12 +227,11 @@ def __str__(self):
228227
o += " continue_fail_tasks: %s\r\n" % (self.continue_fail_tasks)
229228
o += " handshake: %f \r\n" % self.handshake
230229
o += " rtt_history: %s\r\n" % (self.rtt_history)
231-
o += " speed_history: %s\r\n" % (self.speed_history)
232230
o += " adjust_history: %s\r\n" % (self.adjust_history)
233231
if self.version != "1.1":
234232
o += "streams: %d\r\n" % len(self.streams)
235233
o += " rtt: %f\r\n" % (self.rtt)
236-
o += " speed: %f\r\n" % (self.speed)
234+
o += " speed: %f\r\n" % (self.ip_manager.get_speed(self.ip_str))
237235
o += " score: %f\r\n" % (self.get_score())
238236
return o
239237

@@ -250,20 +248,9 @@ def update_rtt(self, rtt, predict_rtt=None):
250248
self.adjust_history.pop(0)
251249

252250
def update_speed(self, speed):
253-
self.speed_history.append(speed)
254-
if len(self.speed_history) > 10:
255-
self.speed_history.pop(0)
256-
self.speed = sum(self.speed_history) / len(self.speed_history)
251+
self.ip_manager.update_speed(self.ip_str, speed)
257252

258253
def update_debug_data(self, rtt, sent, received, speed):
259-
# if sent + received > 10000:
260-
# self.speed_history.append(speed)
261-
# if len(self.speed_history) > 10:
262-
# self.speed_history.pop(0)
263-
# self.speed = sum(self.speed_history) / len(self.speed_history)
264-
# else:
265-
# self.rtt = rtt
266-
267254
self.log_debug_data(rtt, sent, received)
268255
return
269256

@@ -296,23 +283,23 @@ def get_score(self):
296283
if self.processed_tasks == 0 and len(self.streams) == 0:
297284
score /= 3
298285

286+
speed = self.ip_manager.get_speed(self.ip_str)
299287
if self.version == "1.1":
300-
score += self.max_payload / self.speed
301-
return score
302-
303-
response_body_len = self.max_payload
304-
for _, stream in self.streams.items():
305-
if stream.response_body_len == 0:
306-
response_body_len += self.max_payload
307-
else:
308-
response_body_len += stream.response_body_len - stream.task.body_len
309-
score += response_body_len / self.speed
288+
score += self.max_payload / speed
289+
else:
290+
response_body_len = self.max_payload
291+
for _, stream in self.streams.items():
292+
if stream.response_body_len == 0:
293+
response_body_len += self.max_payload
294+
else:
295+
response_body_len += stream.response_body_len - stream.task.body_len
296+
score += response_body_len / speed
310297

311-
score += len(self.streams) * 0.06
298+
score += len(self.streams) * 0.06
312299

313300
if self.config.show_state_debug:
314301
self.logger.debug("get_score %s, speed:%f rtt:%d stream_num:%d score:%f", self.ip_str,
315-
self.speed * 0.000001, self.rtt * 1000, len(self.streams), score)
302+
speed * 0.000001, self.rtt * 1000, len(self.streams), score)
316303

317304
return score
318305

‎code/default/lib/noarch/front_base/http_dispatcher.py

+35-8
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
sorted by rtt and pipeline task on load.
1818
"""
1919
from six.moves import queue
20-
20+
import random
2121
import operator
2222
import threading
2323
import time
@@ -65,6 +65,8 @@ def __init__(self, logger, config, ip_manager, connection_manager,
6565
self.request_queue = Queue()
6666
self.workers = []
6767
self.working_tasks = {}
68+
self.account = ""
69+
self.last_host = None
6870
self.h1_num = 0
6971
self.h2_num = 0
7072
self.last_request_time = time.time()
@@ -95,6 +97,7 @@ def __init__(self, logger, config, ip_manager, connection_manager,
9597
"sent": 0,
9698
"received": 0
9799
}
100+
self.ping_speed_ip_str_last_active = {} # ip_str => last_active
98101

99102
self.trigger_create_worker_cv = SimpleCondition()
100103
self.wait_a_worker_cv = SimpleCondition()
@@ -138,9 +141,34 @@ def on_ssl_created_cb(self, ssl_sock, remove_slowest_worker=True):
138141

139142
self.workers.append(worker)
140143

141-
if remove_slowest_worker:
144+
if time.time() - self.ping_speed_ip_str_last_active.get(worker.ip_str, 0) > self.config.dispather_ping_check_speed_interval:
145+
self.ping_speed(worker)
146+
self.ping_speed_ip_str_last_active[worker.ip_str] = time.time()
147+
148+
elif remove_slowest_worker:
142149
self._remove_slowest_worker()
143150

151+
def ping_speed(self, worker):
152+
if not self.last_host:
153+
return
154+
155+
method = b"POST"
156+
path = b"/ping?content_length=%d" % self.config.dispather_ping_download_size
157+
body = utils.to_bytes(utils.generate_random_lowercase(self.config.dispather_ping_upload_size))
158+
headers = {
159+
b"Padding": utils.to_str(utils.generate_random_lowercase(random.randint(64, 256))),
160+
b"Xx-Account": self.account,
161+
b"X-Host": self.last_host,
162+
b"X-Path": path
163+
}
164+
165+
task = http_common.Task(self.logger, self.config, method, self.last_host, path,
166+
headers, body, None, "/ping", 5)
167+
task.set_state("start_ping_request")
168+
# self.logger.debug("send ping for %s", worker.ip_str)
169+
170+
worker.request(task)
171+
144172
def _on_worker_idle_cb(self):
145173
self.wait_a_worker_cv.notify()
146174

@@ -261,7 +289,7 @@ def get_worker(self, nowait=False):
261289

262290
def _remove_slowest_worker(self):
263291
# close slowest worker,
264-
# give change for better worker
292+
# give chance for better worker
265293
while True:
266294
slowest_score = 9999
267295
slowest_worker = None
@@ -307,6 +335,8 @@ def request(self, method, host, path, headers, body, url=b"", timeout=60):
307335
with self.task_count_lock:
308336
self.task_count += 1
309337

338+
self.last_host = host
339+
310340
try:
311341
if not url:
312342
url = b"%s %s%s" % (method, host, path)
@@ -411,6 +441,7 @@ def dispatcher(self):
411441

412442
get_worker_time = time.time()
413443
get_cost = get_worker_time - get_time
444+
self.ping_speed_ip_str_last_active[worker.ip_str] = get_worker_time
414445
task.set_state("get_worker(%d):%s" % (get_cost, worker.ip_str))
415446
task.worker = worker
416447
task.predict_rtt = worker.get_score()
@@ -524,7 +555,7 @@ def to_string(self):
524555

525556
w_r = sorted(list(worker_rate.items()), key=operator.itemgetter(1))
526557

527-
out_str = 'thread num:%d\r\n' % threading.activeCount()
558+
out_str = 'thread num:%d\r\n' % threading.active_count()
528559
for w, r in w_r:
529560
out_str += "%s score:%d rtt:%d running:%d accept:%d live:%d inactive:%d processed:%d" % \
530561
(w.ip_str, w.get_score(), w.rtt, w.keep_running, w.accept_task,
@@ -536,10 +567,6 @@ def to_string(self):
536567
elif w.version == "1.1":
537568
out_str += " Trace:%s" % w.get_trace()
538569

539-
out_str += "\r\n Speed:"
540-
for speed in w.speed_history:
541-
out_str += "%d," % speed
542-
543570
out_str += "\r\n"
544571

545572
out_str += "\r\n working_tasks:\r\n"

‎code/default/lib/noarch/front_base/ip_manager.py

+46-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#!/usr/bin/env python
22
# -*- coding: utf-8 -*-
3+
import json
34
from six.moves import queue
45
import operator
56
import os
@@ -12,14 +13,56 @@
1213

1314

1415
class IpManagerBase():
15-
def __init__(self, config, ip_source, logger):
16+
def __init__(self, config, ip_source, logger, speed_fn=None):
1617
self.scan_thread_lock = threading.Lock()
1718
self.ip_lock = threading.Lock()
1819

1920
self.config = config
2021
self.ip_source = ip_source
2122
self.logger = logger
2223
self.ips = []
24+
self.speed_fn = speed_fn
25+
self.speed_info = self.load_speed_info()
26+
self.speed_info_last_save_time = time.time()
27+
28+
def load_speed_info(self):
29+
if not self.speed_fn or not os.path.isfile(self.speed_fn):
30+
return {}
31+
32+
try:
33+
with open(self.speed_fn, "r") as fd:
34+
info = json.load(fd)
35+
return info
36+
except Exception as e:
37+
self.logger.exception("load speed info %s failed:%r", self.speed_fn, e)
38+
return {}
39+
40+
def save_speed_info(self):
41+
if not self.speed_fn:
42+
return
43+
44+
try:
45+
with open(self.speed_fn, "w") as fd:
46+
json.dump(self.speed_info, fd, indent=2)
47+
except Exception as e:
48+
self.logger.exception("save speed info %s fail:%r", self.speed_fn, e)
49+
50+
def update_speed(self, ip_str, speed):
51+
ip_str = utils.to_str(ip_str)
52+
ip_info = self.speed_info.setdefault(ip_str, {})
53+
ip_info.setdefault("history", []).append(speed)
54+
if len(ip_info["history"]) > self.config.ip_speed_history_size:
55+
ip_info["history"].pop(0)
56+
ip_info["speed"] = sum(ip_info["history"]) / len(ip_info["history"])
57+
# self.logger.debug("update speed %s %d", ip_str, ip_info["speed"])
58+
59+
if time.time() - self.speed_info_last_save_time > self.config.ip_speed_save_interval:
60+
self.save_speed_info()
61+
self.speed_info_last_save_time = time.time()
62+
63+
def get_speed(self, ip_str):
64+
ip_str = utils.to_str(ip_str)
65+
return self.speed_info.get(ip_str, {}).get("speed", self.config.ip_initial_speed)
2366

2467
def load_config(self):
2568
pass
@@ -62,7 +105,7 @@ def recheck_ip(self, ip_str):
62105
# good case is 60ms
63106
# bad case is 1300ms and more.
64107

65-
class IpManager():
108+
class IpManager(IpManagerBase):
66109
# Functions:
67110
# 1. Scan ip in back ground
68111
# 2. sort ip by RTT and fail times
@@ -76,9 +119,7 @@ class IpManager():
76119

77120
def __init__(self, logger, config, ip_source, host_manager, check_local_network, check_ip,
78121
default_ip_list_fn, ip_list_fn, scan_ip_log=None):
79-
self.logger = logger
80-
self.config = config
81-
self.ip_source = ip_source
122+
super().__init__(config, ip_source, logger)
82123
self.host_manager = host_manager
83124
self.check_local_network = check_local_network
84125
self.check_ip = check_ip

‎code/default/lib/noarch/utils.py

+8
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,14 @@ def get_ip_port(ip_str, port=443):
9696
return ip, int(port)
9797

9898

99+
def get_ip_str(ip, port=443):
100+
ip = to_str(ip)
101+
if ":" in ip:
102+
ip = "[" + ip + "]"
103+
ip_str = ip + ":" + str(port)
104+
return ip_str
105+
106+
99107
domain_allowed = re.compile("(?!-)[A-Z\d-]{1,63}(?<!-)$")
100108

101109

‎code/default/smart_router/local/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ def load_config():
8484
config.set_var("auto_direct6", 0)
8585
config.set_var("auto_gae", 1)
8686
config.set_var("enable_fake_ca", 1)
87+
config.set_var("bypass_speedtest", 1)
8788
config.set_var("block_advertisement", 0)
8889

8990
config.set_var("log_debug", 0)

‎code/default/smart_router/local/gfwlist.py

+9
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@
2626

2727
data_path = os.path.join(env_info.data_path, "smart_router")
2828

29+
2930
class GfwList(object):
3031
def __init__(self):
3132
self.gfw_black_list = utils.to_bytes(self.load("gfw_black_list.txt"))
3233
self.gfw_white_list = utils.to_bytes(self.load("gfw_white_list.txt"))
34+
self.speedtest_whitelist = utils.to_bytes(self.load("speedtest_whitelist.txt"))
3335
self.advertisement_list = utils.to_bytes(self.load("advertisement_list.txt"))
3436
# xlog.debug("white_list size:%d mem:%d", len(self.gfw_white_list), sys.getsizeof(self.gfw_white_list))
3537
# xlog.debug("black_list size:%d mem:%d", len(self.gfw_black_list), sys.getsizeof(self.gfw_black_list))
@@ -70,6 +72,13 @@ def in_white_list(self, host):
7072
else:
7173
return False
7274

75+
def in_speedtest_whitelist(self, host):
76+
dot_host = b"." + host
77+
if dot_host.endswith(self.speedtest_whitelist):
78+
return True
79+
else:
80+
return False
81+
7382
def is_advertisement(self, host):
7483
dot_host = b"." + host
7584
if dot_host.endswith(self.advertisement_list):

‎code/default/smart_router/local/smart_route.py

+4
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,10 @@ def handle_domain_proxy(sock, host, port, client_address, left_buf=""):
555555
elif utils.check_ip_valid(host) and utils.is_private_ip(host):
556556
rule = "direct"
557557

558+
if not rule and (g.config.bypass_speedtest and g.gfwlist.in_speedtest_whitelist(host)):
559+
xlog.debug("speedtest %s", host)
560+
rule = "direct"
561+
558562
if rule:
559563
return try_loop("domain user", [rule], sock, host, port, client_address, left_buf)
560564

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
cdnst.net
2+
cellmaps.com
3+
ekahau.cloud
4+
ekahau.com
5+
ookla.com
6+
ooklaserver.net
7+
pingtest.net
8+
speedtest.co
9+
speedtest.net
10+
speedtestcustom.com
11+
webtest.net
12+
measurement-lab.org
13+
measurementlab.net

‎code/default/smart_router/local/web_control.py

+3
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ def req_config_handler(self):
107107
"auto_direct6":g.config.auto_direct6,
108108
"auto_gae": g.config.auto_gae,
109109
"enable_fake_ca": g.config.enable_fake_ca,
110+
"bypass_speedtest": g.config.bypass_speedtest,
110111
"block_advertisement": g.config.block_advertisement
111112
}
112113
return self.response_json(data)
@@ -127,6 +128,8 @@ def req_config_handler(self):
127128
g.config.auto_gae = int(reqs["auto_gae"])
128129
if "enable_fake_ca" in reqs:
129130
g.config.enable_fake_ca = int(reqs["enable_fake_ca"])
131+
if "bypass_speedtest" in reqs:
132+
g.config.bypass_speedtest = int(reqs["bypass_speedtest"])
130133
if "block_advertisement" in reqs:
131134
g.config.block_advertisement = int(reqs["block_advertisement"])
132135
g.config.save()

‎code/default/smart_router/web_ui/config_general.html

+17
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@
5555
</div> <!-- .span8 -->
5656
</div> <!-- .row-fluid -->
5757

58+
<div class="row-fluid">
59+
<div class="config_label">{{ _( "Bypass Speed Test Sites" ) }}</div> <!-- .span4 -->
60+
<div class="config_switch">
61+
<input id="bypass_speedtest" type="checkbox" data-toggle="switch"/>
62+
</div> <!-- .span8 -->
63+
</div> <!-- .row-fluid -->
64+
5865
<div class="row-fluid">
5966
<div class="config_label">{{ _( "Block Advertisement" ) }}</div> <!-- .span4 -->
6067
<div class="config_switch">
@@ -109,6 +116,12 @@
109116

110117
$("#enable_fake_ca").prop('checked', true);
111118
}
119+
if (result['bypass_speedtest'] != 0) {
120+
$("#bypass_speedtest").parent().removeClass('switch-off');
121+
$("#bypass_speedtest").parent().addClass('switch-on');
122+
123+
$("#bypass_speedtest").prop('checked', true);
124+
}
112125
if (result['block_advertisement'] != 0) {
113126
$("#block_advertisement").parent().removeClass('switch-off');
114127
$("#block_advertisement").parent().addClass('switch-on');
@@ -175,6 +188,10 @@
175188
setSmartRouterConfig("enable_fake_ca", $(this).is(':checked') ? 1 : 0);
176189
});
177190

191+
$('#bypass_speedtest').change(function () {
192+
setSmartRouterConfig("bypass_speedtest", $(this).is(':checked') ? 1 : 0);
193+
});
194+
178195
$('#block_advertisement').change(function () {
179196
setSmartRouterConfig("block_advertisement", $(this).is(':checked') ? 1 : 0);
180197
});

‎code/default/version.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
5.7.7
1+
5.8.1

‎code/default/x_tunnel/local/cloudflare_front/front.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class Front(object):
2828
name = "cloudflare_front"
2929

3030
def __init__(self):
31+
self.running = False
3132
self.logger = logger
3233
config_path = os.path.join(module_data_path, "cloudflare_front.json")
3334
self.config = Config(config_path)
@@ -44,7 +45,8 @@ def start(self):
4445
ca_certs = os.path.join(current_path, "cacert.pem")
4546
default_domain_fn = os.path.join(current_path, "front_domains.json")
4647
domain_fn = os.path.join(module_data_path, "cloudflare_domains.json")
47-
self.ip_manager = ip_manager.IpManager(self.config, default_domain_fn, domain_fn, self.logger)
48+
ip_speed_fn = os.path.join(module_data_path, "cloudflare_speed.json")
49+
self.ip_manager = ip_manager.IpManager(self.config, default_domain_fn, domain_fn, ip_speed_fn, self.logger)
4850

4951
openssl_context = SSLContext(logger, ca_certs=ca_certs)
5052
self.connect_creator = ConnectCreator(logger, self.config, openssl_context, None)

‎code/default/x_tunnel/local/cloudflare_front/ip_manager.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99

1010

1111
class IpManager(IpManagerBase):
12-
def __init__(self, config, default_domain_fn, domain_fn, logger):
13-
super(IpManager, self).__init__(config, None, logger)
12+
def __init__(self, config, default_domain_fn, domain_fn, speed_fn, logger):
13+
super(IpManager, self).__init__(config, None, logger, speed_fn)
1414
self.default_domain_fn = default_domain_fn
1515
self.domain_fn = domain_fn
1616

‎code/default/x_tunnel/local/cloudflare_front/web_control.py

+3
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ def req_ip_list_handler(self):
125125
self.send_response_nc(mimetype, data)
126126

127127
def req_debug_handler(self):
128+
if not front.running:
129+
return self.send_response_nc('text/plain', "Not running")
130+
128131
data = ""
129132
objs = [front.connect_manager] + list(front.dispatchs.values())
130133
for obj in objs:

‎code/default/x_tunnel/local/proxy_session.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ def create_conn(self, sock, host, port, log=False):
454454
g.config.windows_ack, True, xlog)
455455

456456
self.target_on_roads = \
457-
min(g.config.concurent_thread_num - g.config.min_on_road, self.target_on_roads + 5)
457+
min(g.config.concurent_thread_num - g.config.min_on_road, self.target_on_roads + 10)
458458
self.trigger_more()
459459

460460
if log:

‎code/default/x_tunnel/local/seley_front/config.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def __init__(self, fn):
1212
self.set_var("allow_set_hosts", 1)
1313

1414
# http_dispatcher
15-
self.set_var("dispather_min_idle_workers", 0)
15+
# self.set_var("dispather_min_idle_workers", 0)
1616
self.set_var("dispather_work_min_idle_time", 0)
1717
self.set_var("dispather_work_max_score", 20000)
1818
self.set_var("dispather_max_workers", 20)
@@ -26,8 +26,8 @@ def __init__(self, fn):
2626
self.set_var("http2_status_to_close", [404])
2727

2828
# connect_manager
29-
self.set_var("connection_pool_min", 0)
30-
self.set_var("https_new_connect_num", 0)
31-
self.set_var("max_links_per_ip", 20)
29+
self.set_var("connection_pool_min", 1)
30+
self.set_var("max_links_per_ip", 17)
31+
self.set_var("connect_create_interval", 0)
3232

3333
self.load()

‎code/default/x_tunnel/local/seley_front/front.py

+6-2
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,21 @@
1212

1313
from .config import Config
1414
from .connect_creator import ConnectCreator
15-
from .ip_manager import IpManager
1615

1716
data_path = env_info.data_path
1817
module_data_path = os.path.join(data_path, 'x_tunnel')
1918

2019
logger = xlog.getLogger("seley_front", log_path=module_data_path, save_start_log=1500, save_warning_log=True)
2120
logger.set_buffer(300)
2221

22+
from .ip_manager import IpManager
23+
2324

2425
class Front(object):
2526
name = "seley_front"
2627

2728
def __init__(self):
29+
self.running = False
2830
self.account = ""
2931
self.password = ""
3032
self.logger = logger
@@ -36,7 +38,8 @@ def start(self):
3638
self.connect_creator = ConnectCreator(logger, self.config)
3739

3840
hosts_fn = os.path.join(module_data_path, "seley_host.json")
39-
self.ip_manager = IpManager(self.config, logger, hosts_fn)
41+
ip_speed_fn = os.path.join(module_data_path, "seley_speed.json")
42+
self.ip_manager = IpManager(self.config, logger, hosts_fn, ip_speed_fn)
4043

4144
self.connect_manager = ConnectManager(logger, self.config, self.connect_creator, self.ip_manager,
4245
check_local_network)
@@ -45,6 +48,7 @@ def start(self):
4548
def set_x_tunnel_account(self, account, password):
4649
self.account = account
4750
self.password = password
51+
self.http_dispatcher.account = account
4852

4953
def set_hosts(self, hosts):
5054
if not self.config.allow_set_hosts:

‎code/default/x_tunnel/local/seley_front/ip_manager.py

+37-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import time
22
import os
33
import json
4+
import socket
45

56
from front_base.ip_manager import IpManagerBase
67
import utils
@@ -9,23 +10,25 @@
910

1011

1112
class IpManager(IpManagerBase):
12-
def __init__(self, config, logger, config_fn):
13-
super().__init__(config, None, logger)
13+
def __init__(self, config, logger, config_fn, speed_fn):
14+
super().__init__(config, None, logger, speed_fn)
1415
self.config_fn = config_fn
1516
self.hosts = {}
1617
self.ip_dict = {}
1718
self.load()
1819

1920
def __str__(self):
2021
o = ""
22+
o += " seley_host: \r\n%s\r\n" % json.dumps(self.hosts, indent=2)
2123
o += " seley_dict: \r\n%s\r\n" % json.dumps(self.ip_dict, indent=2)
24+
o += " speed_info: \r\n%s\r\n" % json.dumps(self.speed_info, indent=2)
2225
return o
2326

2427
def set_hosts(self, hosts):
2528
self.hosts = hosts
2629
try:
2730
with open(self.config_fn, "w") as fd:
28-
json.dump(self.hosts, fd)
31+
json.dump(self.hosts, fd, indent=2)
2932
except Exception as e:
3033
xlog.error("save hosts %s e:%r", self.config_fn, e)
3134

@@ -35,9 +38,27 @@ def load(self):
3538

3639
try:
3740
with open(self.config_fn, "r") as fd:
38-
self.hosts = json.load(fd)
41+
domain_hosts = json.load(fd)
3942
except Exception as e:
4043
xlog.warn("load hosts %s e:%r", self.config_fn, e)
44+
return
45+
46+
ip_hosts = {}
47+
for domain_port, host_info in domain_hosts.items():
48+
ip, port = utils.get_ip_port(domain_port)
49+
if not utils.check_ip_valid(ip):
50+
try:
51+
info = socket.getaddrinfo(ip, port, socket.AF_UNSPEC,
52+
socket.SOCK_STREAM)
53+
54+
af, socktype, proto, canonname, sa = info[0]
55+
ip = sa[0]
56+
except socket.gaierror:
57+
pass
58+
59+
ip_str = utils.get_ip_str(ip, port)
60+
ip_hosts[ip_str] = host_info
61+
self.hosts = ip_hosts
4162

4263
def _get_ip_info(self, ip_str):
4364
ip_str = utils.to_str(ip_str)
@@ -54,7 +75,7 @@ def get_ip_sni_host(self):
5475
now = time.time()
5576

5677
best_info = None
57-
best_rtt = 99999
78+
best_speed = 0
5879

5980
for ip_str, params in self.hosts.items():
6081
if not params.get("key"):
@@ -71,16 +92,23 @@ def get_ip_sni_host(self):
7192
if info["fail_times"] and now - info["last_try"] < 60:
7293
continue
7394

74-
if info["rtt"] < best_rtt:
75-
best_rtt = info["rtt"]
95+
if now - info["last_try"] > 30 * 60:
96+
best_info = info
97+
xlog.debug("get_ip_sni_host last_try %s", ip_str)
98+
break
99+
100+
speed = self.get_speed(ip_str)
101+
if speed > best_speed:
102+
best_speed = speed
76103
best_info = info
104+
xlog.debug("get_ip_sni_host best speed %s", ip_str)
77105

78106
if not best_info:
79107
return None, None, None
80108

81-
best_info["links"] += 1
82-
best_info["last_try"] = now
83109
ip_str = best_info["ip_str"]
110+
self.ip_dict[ip_str]["links"] += 1
111+
self.ip_dict[ip_str]["last_try"] = now
84112
key = self.hosts[ip_str]["key"]
85113

86114
return best_info["ip_str"], key, ip_str
@@ -89,7 +117,6 @@ def update_ip(self, ip_str, sni, handshake_time):
89117
info = self._get_ip_info(ip_str)
90118
info["fail_times"] = 0
91119
info["rtt"] = handshake_time
92-
info["last_try"] = 0.0
93120
# self.logger.debug("ip %s connect success", ip)
94121

95122
def report_connect_fail(self, ip_str, sni=None, reason="", force_remove=False):

‎code/default/x_tunnel/local/seley_front/web_control.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ def req_log_handler(self):
6464
self.send_response_nc(mimetype, data)
6565

6666
def req_debug_handler(self):
67+
if not front.running:
68+
return self.send_response_nc('text/plain', "Not running")
69+
6770
data = ""
6871
for obj in [front.connect_manager, front.http_dispatcher]:
6972
data += "%s\r\n" % obj.__class__
@@ -73,7 +76,17 @@ def req_debug_handler(self):
7376
sub_obj = getattr(obj, attr)
7477
if callable(sub_obj):
7578
continue
76-
data += " %s = %s\r\n" % (attr, sub_obj)
79+
80+
if isinstance(sub_obj, list):
81+
data += " %s:\r\n" % (attr)
82+
for item in sub_obj:
83+
data += " %s\r\n" % item
84+
data += "\r\n"
85+
elif hasattr(sub_obj, "to_string"):
86+
data += " %s:\r\n" % (attr)
87+
data += sub_obj.to_string()
88+
else:
89+
data += " %s = %s\r\n" % (attr, sub_obj)
7790
if hasattr(obj, "to_string"):
7891
data += obj.to_string()
7992

‎code/default/x_tunnel/local/tls_relay_front/front.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class Front(object):
2727
name = "tls_relay_front"
2828

2929
def __init__(self):
30+
self.running = False
3031
self.logger = logger
3132
config_path = os.path.join(module_data_path, "tls_relay.json")
3233
self.config = Config(config_path)
@@ -44,7 +45,8 @@ def start(self):
4445

4546
self.connect_creator = connect_creator.ConnectCreator(logger, self.config, self.openssl_context, self.host_manager)
4647

47-
self.ip_manager = ip_manager.IpManager(self.config, self.host_manager, logger)
48+
ip_speed_fn = os.path.join(module_data_path, "relay_speed.json")
49+
self.ip_manager = ip_manager.IpManager(self.config, self.host_manager, logger, ip_speed_fn)
4850
self.connect_manager = ConnectManager(logger, self.config, self.connect_creator, self.ip_manager, check_local_network)
4951
self.http_dispatcher = HttpsDispatcher(logger, self.config, self.ip_manager, self.connect_manager,
5052
http2stream_class=http2_stream.Stream)
@@ -58,6 +60,7 @@ def get_dispatcher(self, host=None):
5860
def set_x_tunnel_account(self, account, password):
5961
self.account = account
6062
self.password = password
63+
self.http_dispatcher.account = account
6164

6265
def set_ips(self, ips):
6366
if not self.config.allow_set_ips:

‎code/default/x_tunnel/local/tls_relay_front/ip_manager.py

+11-9
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77

88
class IpManager(IpManagerBase):
9-
def __init__(self, config, host_manager, logger):
10-
super(IpManager, self).__init__(config, None, logger)
9+
def __init__(self, config, host_manager, logger, speed_fn):
10+
super(IpManager, self).__init__(config, None, logger, speed_fn)
1111
self.host_manager = host_manager
1212
self.ip_dict = {}
1313

@@ -31,9 +31,12 @@ def get_ip_sni_host(self):
3131
ips = self.host_manager.ips
3232

3333
best_info = None
34-
best_rtt = 99999
34+
best_speed = 0
3535

3636
for ip in ips:
37+
port = int(self.host_manager.info[ip].get("port", 443))
38+
ip_str = utils.get_ip_str(ip, port)
39+
3740
info = self._get_ip_info(ip)
3841
if info["links"] < 0:
3942
# self.logger.error("ip %s link:%d", ip, info["links"])
@@ -45,8 +48,9 @@ def get_ip_sni_host(self):
4548
if info["fail_times"] and now - info["last_try"] < 60:
4649
continue
4750

48-
if info["rtt"] < best_rtt:
49-
best_rtt = info["rtt"]
51+
speed = self.get_speed(ip_str)
52+
if speed > best_speed:
53+
best_speed = speed
5054
best_info = info
5155

5256
if not best_info:
@@ -58,10 +62,8 @@ def get_ip_sni_host(self):
5862

5963
ip = best_info["ip"]
6064
port = int(self.host_manager.info[ip].get("port", 443))
61-
if ":" in ip:
62-
ip = "[" + ip + "]"
63-
return ip + ":" + str(port), None, None
64-
65+
ip_str = utils.get_ip_str(ip, port)
66+
return ip_str, None, None
6567

6668
def update_ip(self, ip_str, sni, handshake_time):
6769
ip, _ = utils.get_ip_port(ip_str)

‎code/default/x_tunnel/local/tls_relay_front/web_control.py

+3
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,9 @@ def req_ip_list_handler(self):
125125
self.send_response_nc(mimetype, data)
126126

127127
def req_debug_handler(self):
128+
if not front.running:
129+
return self.send_response_nc('text/plain', "Not running")
130+
128131
data = ""
129132
for obj in [front.connect_manager, front.http_dispatcher]:
130133
data += "%s\r\n" % obj.__class__

‎code/default/x_tunnel/local/upload_logs.py

+51-7
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@
55
import zipfile
66
import operator
77

8+
import simple_http_client
89
import env_info
910
import utils
1011
from xlog import getLogger, reset_log_files
1112
xlog = getLogger("x_tunnel")
1213

14+
from x_tunnel.local import global_var as g
15+
1316
current_path = os.path.dirname(os.path.abspath(__file__))
1417
root_path = os.path.abspath(os.path.join(current_path, os.pardir, os.pardir))
1518
data_path = env_info.data_path
1619
data_xtunnel_path = os.path.join(data_path, 'x_tunnel')
17-
g = None
1820

1921

2022
def sleep(t):
@@ -38,6 +40,51 @@ def mask_x_tunnel_password(fp):
3840
return dat_str
3941

4042

43+
def collect_debug_and_log():
44+
# collect debug info and save to folders
45+
debug_infos = {
46+
"system_info": "http://127.0.0.1:8085/debug",
47+
"gae_info": "http://127.0.0.1:8085/module/gae_proxy/control/debug",
48+
"gae_log": "http://127.0.0.1:8085/module/gae_proxy/control/log?cmd=get_new&last_no=1",
49+
"xtunnel_info": "http://127.0.0.1:8085/module/x_tunnel/control/debug",
50+
"xtunnel_status": "http://127.0.0.1:8085/module/x_tunnel/control/status",
51+
"cloudflare_info": "http://127.0.0.1:8085/module/x_tunnel/control/cloudflare_front/debug",
52+
"tls_info": "http://127.0.0.1:8085/module/x_tunnel/control/tls_relay_front/debug",
53+
"seley_info": "http://127.0.0.1:8085/module/x_tunnel/control/seley_front/debug",
54+
"cloudflare_log": "http://127.0.0.1:8085/module/x_tunnel/control/cloudflare_front/log?cmd=get_new&last_no=1",
55+
"tls_log": "http://127.0.0.1:8085/module/x_tunnel/control/tls_relay_front/log?cmd=get_new&last_no=1",
56+
"seley_log": "http://127.0.0.1:8085/module/x_tunnel/control/seley_front/log?cmd=get_new&last_no=1",
57+
"xtunnel_log": "http://127.0.0.1:8085/module/x_tunnel/control/log?cmd=get_new&last_no=1",
58+
"smartroute_log": "http://127.0.0.1:8085/module/smart_router/control/log?cmd=get_new&last_no=1",
59+
"launcher_log": "http://127.0.0.1:8085/log?cmd=get_new&last_no=1"
60+
}
61+
62+
download_path = os.path.join(env_info.data_path, "downloads")
63+
if not os.path.isdir(download_path):
64+
os.mkdir(download_path)
65+
66+
for name, url in debug_infos.items():
67+
# xlog.debug("fetch %s %s", name, url)
68+
try:
69+
res = simple_http_client.request("GET", url, timeout=1)
70+
if name.endswith("log"):
71+
dat = json.loads(res.text)
72+
no_line = list(dat.items())
73+
no_line = [[int(line[0]), line[1]] for line in no_line]
74+
no_line = sorted(no_line, key=operator.itemgetter(0))
75+
lines = [line[1] for line in no_line]
76+
data = "".join(lines)
77+
data = utils.to_bytes(data)
78+
else:
79+
data = res.text
80+
81+
fn = os.path.join(download_path, name + ".txt")
82+
with open(fn, "wb") as fd:
83+
fd.write(data)
84+
except Exception as e:
85+
xlog.warn("fetch info %s fail:%r", url, e)
86+
87+
4188
def list_files():
4289
log_files = {}
4390
other_files = []
@@ -65,6 +112,9 @@ def list_files():
65112

66113
def pack_logs(max_size=800 * 1024):
67114
content_size = 0
115+
116+
collect_debug_and_log()
117+
68118
try:
69119
files = list_files()
70120
zip_buffer = io.BytesIO()
@@ -93,12 +143,6 @@ def pack_logs(max_size=800 * 1024):
93143

94144

95145
def upload_logs_thread():
96-
global g
97-
98-
if not g:
99-
from . import global_var
100-
g = global_var
101-
102146
sleep(3 * 60)
103147
while g.running:
104148
if not g.running or not g.server_host or not g.session or g.session.last_receive_time == 0:

‎code/default/x_tunnel/web_ui/status.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ <h4>{{ _( "TLS_Relay" ) }} {{ _( "Front" ) }}</h4>
180180
</tbody>
181181
</table>
182182

183-
<h4>{{ _( "TLS_Relay" ) }} {{ _( "Front" ) }}</h4>
183+
<h4>{{ _( "Seley" ) }} {{ _( "Front" ) }}</h4>
184184
<table id="seley_front" class="table table-bordered table-striped">
185185
<thead>
186186
<tr>
@@ -211,6 +211,7 @@ <h4>{{ _( "TLS_Relay" ) }} {{ _( "Front" ) }}</h4>
211211
</tr>
212212
</tbody>
213213
</table>
214+
214215
</div> <!-- #details -->
215216

216217
<!-- JavaScript -->

0 commit comments

Comments
 (0)
Please sign in to comment.