@@ -306,6 +306,8 @@ def _parse_vmess_link(self, link: str) -> dict:
306306 try :
307307 # Remove "vmess://" and decode the base64 content
308308 b64_content = link [8 :]
309+ # Add padding if necessary
310+ b64_content += "=" * (- len (b64_content ) % 4 )
309311 decoded_content = base64 .b64decode (b64_content ).decode ("utf-8" )
310312 vmess_info = json .loads (decoded_content )
311313
@@ -357,11 +359,14 @@ def _parse_vless_link(self, link: str) -> dict:
357359 parsed_url = urllib .parse .urlparse (link )
358360
359361 # Extract user info (uuid)
360- user_info = parsed_url .netloc .split ("@" )[0 ]
362+ if "@" not in parsed_url .netloc :
363+ raise ValueError ("Invalid VLESS link: missing user info" )
364+ user_info , host_port = parsed_url .netloc .split ("@" , 1 )
361365
362366 # Extract host and port
363- host_port = parsed_url .netloc .split ("@" )[1 ]
364- host , port = host_port .split (":" )
367+ if ":" not in host_port :
368+ raise ValueError ("Invalid VLESS link: missing port" )
369+ host , port = host_port .rsplit (":" , 1 )
365370
366371 # Parse query parameters
367372 params = dict (urllib .parse .parse_qsl (parsed_url .query ))
@@ -370,18 +375,18 @@ def _parse_vless_link(self, link: str) -> dict:
370375 outbound = {
371376 "protocol" : "vless" ,
372377 "settings" : {
373- "vnext" : [{"address" : host , "port" : int (port ), "users" : [{"id" : user_info , "encryption" : " none" , "level" : 0 }]}]
378+ "vnext" : [{"address" : host , "port" : int (port ), "users" : [{"id" : user_info , "encryption" : params . get ( "encryption" , " none") , "level" : 0 }]}]
374379 },
375380 "streamSettings" : {"network" : params .get ("type" , "tcp" ), "security" : params .get ("security" , "none" )},
376381 }
377382
378383 # Handle TLS settings
379384 if params .get ("security" ) == "tls" :
380- outbound ["streamSettings" ]["tlsSettings" ] = {"serverName" : params .get ("sni" , "" )}
385+ outbound ["streamSettings" ]["tlsSettings" ] = {"serverName" : params .get ("sni" , host )}
381386
382387 # Handle WebSocket settings
383388 if params .get ("type" ) == "ws" :
384- outbound ["streamSettings" ]["wsSettings" ] = {"path" : params .get ("path" , "/" ), "headers" : {"Host" : params .get ("host" , "" )}}
389+ outbound ["streamSettings" ]["wsSettings" ] = {"path" : params .get ("path" , "/" ), "headers" : {"Host" : params .get ("host" , host )}}
385390
386391 return outbound
387392 except Exception as e :
@@ -394,29 +399,34 @@ def _parse_shadowsocks_link(self, link: str) -> dict:
394399 raise ValueError ("Not a valid Shadowsocks link" )
395400
396401 try :
397- # Two possible formats:
398- # 1. ss://base64(method:password@host:port)#remark
399- # 2. ss://base64(method:password)@host:port#remark
400-
401402 parsed_url = urllib .parse .urlparse (link )
403+ remark = urllib .parse .unquote (parsed_url .fragment ) if parsed_url .fragment else ""
402404
403405 if "@" in parsed_url .netloc :
404- # Format 2
406+ # Format: ss://base64(method:password)@host:port#remark
405407 user_info_b64 , host_port = parsed_url .netloc .split ("@" , 1 )
408+ user_info_b64 += "=" * (- len (user_info_b64 ) % 4 )
406409 user_info = base64 .b64decode (user_info_b64 ).decode ("utf-8" )
407410 method , password = user_info .split (":" , 1 )
408- host , port = host_port .split (":" , 1 )
411+ host , port = host_port .rsplit (":" , 1 )
409412 else :
410- # Format 1
411- decoded = base64 .b64decode (parsed_url .netloc ).decode ("utf-8" )
413+ # Format: ss://base64(method:password@host:port)#remark
414+ decoded_part = parsed_url .netloc
415+ decoded_part += "=" * (- len (decoded_part ) % 4 )
416+ decoded = base64 .b64decode (decoded_part ).decode ("utf-8" )
417+
418+ if "@" not in decoded :
419+ raise ValueError ("Invalid Shadowsocks link format" )
420+
412421 method_pass , host_port = decoded .split ("@" , 1 )
413422 method , password = method_pass .split (":" , 1 )
414- host , port = host_port .split (":" , 1 )
423+ host , port = host_port .rsplit (":" , 1 )
415424
416425 # Create outbound configuration
417426 outbound = {
418427 "protocol" : "shadowsocks" ,
419428 "settings" : {"servers" : [{"address" : host , "port" : int (port ), "method" : method , "password" : password }]},
429+ "tag" : remark
420430 }
421431
422432 return outbound
@@ -433,12 +443,17 @@ def _parse_trojan_link(self, link: str) -> dict:
433443 # Format: trojan://password@host:port?param=value¶m2=value2#remark
434444 parsed_url = urllib .parse .urlparse (link )
435445
446+ if "@" not in parsed_url .netloc :
447+ raise ValueError ("Invalid Trojan link: missing password or host" )
448+
436449 # Extract password
437- password = parsed_url .netloc .split ("@" )[0 ]
450+ password , host_port = parsed_url .netloc .split ("@" , 1 )
451+ password = urllib .parse .unquote (password )
438452
439453 # Extract host and port
440- host_port = parsed_url .netloc .split ("@" )[1 ]
441- host , port = host_port .split (":" )
454+ if ":" not in host_port :
455+ raise ValueError ("Invalid Trojan link: missing port" )
456+ host , port = host_port .rsplit (":" , 1 )
442457
443458 # Parse query parameters
444459 params = dict (urllib .parse .parse_qsl (parsed_url .query ))
@@ -449,19 +464,29 @@ def _parse_trojan_link(self, link: str) -> dict:
449464 "settings" : {"servers" : [{"address" : host , "port" : int (port ), "password" : password }]},
450465 "streamSettings" : {
451466 "network" : params .get ("type" , "tcp" ),
452- "security" : " tls",
467+ "security" : params . get ( "security" , " tls"), # Default to tls for trojan
453468 "tlsSettings" : {"serverName" : params .get ("sni" , host )},
454469 },
455470 }
471+
472+ if params .get ("type" ) == "ws" :
473+ outbound ["streamSettings" ]["wsSettings" ] = {
474+ "path" : params .get ("path" , "/" ),
475+ "headers" : {"Host" : params .get ("host" , host )}
476+ }
456477
457478 return outbound
458479 except Exception as e :
459480 logging .error (f"Failed to parse Trojan link: { str (e )} " )
460481 raise ValueError (f"Invalid Trojan format: { str (e )} " )
461482
483+ def _prepare_link (self ):
484+ ...
485+
462486 def generate_config (self ):
463487 """Generate V2Ray configuration from link."""
464488 try :
489+ # self.v2ray_link._prepare_link()
465490 # Determine the type of link and parse accordingly
466491 if self .v2ray_link .startswith ("vmess://" ):
467492 outbound = self ._parse_vmess_link (self .v2ray_link )
@@ -539,7 +564,7 @@ def _check_proxy_ready(self, timeout=15):
539564 last_error = str (e )
540565 logging .debug (f"Proxy not ready yet: { last_error } " )
541566
542- time .sleep (1 )
567+ time .sleep (0.01 )
543568
544569 # If we get here, the proxy didn't become ready in time
545570 if self .v2ray_process .poll () is not None :
@@ -776,7 +801,7 @@ def _terminate_windows_process(self, pid, timeout):
776801 # Fallback to subprocess if psutil not available
777802 try :
778803 subprocess .run (["taskkill" , "/F" , "/T" , "/PID" , str (pid )], check = False , capture_output = True , timeout = timeout )
779- time .sleep (0.1 )
804+ time .sleep (0.01 )
780805 if self .v2ray_process .poll () is not None :
781806 self ._process_terminated .set ()
782807 return True
@@ -929,7 +954,7 @@ def usage_memory(self):
929954 @property
930955 def usage_memory_mb (self ):
931956 """Get the memory usage of the V2Ray process in MB."""
932- return self .memory_usage / (1024 * 1024 )
957+ return self .usage_memory / (1024 * 1024 )
933958
934959 @property
935960 def usage_cpu (self ):
@@ -1144,8 +1169,7 @@ def restart_proxy(self, proxy_id):
11441169 # Stop the proxy
11451170 self .proxies [proxy_id ].stop ()
11461171
1147- # Wait a bit to ensure clean shutdown
1148- time .sleep (1 )
1172+ time .sleep (0.02 )
11491173
11501174 # Start the proxy again
11511175 self .proxies [proxy_id ].start ()
0 commit comments