You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Gateway/Bridge/HttpBridgeClient.cs

389 lines
14 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System.Linq;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Net;
using System.Net.Sockets;
using System.Text;
using UMC.Data;
using UMC.Net;
using UMC.Web;
namespace UMC.Proxy
{
class HttpBridgeClient : NetBridge, UMC.Host.IDoWorker
{
private static ConcurrentDictionary<int, HttpBridgeClient> _bridgeClients = new ConcurrentDictionary<int, HttpBridgeClient>();
//
// System.Collections.Concurrent.ConcurrentDictionary
public static void Start(String key, String host, int port, int count)
{
if (_bridgeClients.Count > 0)
{
var vs = _bridgeClients.Values.ToArray();
foreach (var vi in vs)
{
vi._Stop();
}
_bridgeClients.Clear();
}
for (var i = 0; i < count; i++)
{
try
{
Connect(key, host, port, i, true);
}
catch
{
break;
}
}
}
String host, ip;
int port, index;
static void Connect(String host, String ip, int port, int index, bool start)
{
var client = new Socket(SocketType.Stream, ProtocolType.Tcp);
client.Connect(ip, port);
var buffers = new byte[2000];
var writer = new UMC.Net.TextWriter((b, c, l) => client.Send(b, c, l, SocketFlags.None), buffers);
writer.Write($"GET / HTTP/1.1\r\n");
writer.Write("Connection: upgrade\r\n");
writer.Write("umc-request-protocol: bridge\r\n");
writer.Write("Upgrade: websocket\r\n");
if (index == 0 && start)
{
writer.Write($"umc-bridge-number: -1\r\n");
}
else
{
writer.Write($"umc-bridge-number: {index}\r\n");
}
writer.Write($"Host: {host}\r\n\r\n");
writer.Flush();
writer.Dispose();
int i = client.Receive(buffers);
var end = UMC.Data.Utility.FindIndex(buffers, 0, i, UMC.Net.MimeResponse.HeaderEnd);
if (end > -1)
{
var headSize = end + 4;
HttpStatusCode m_StatusCode;
var header = new NameValueCollection();
if (ResponseHeader(buffers, 0, headSize, header, out m_StatusCode))
{
if (m_StatusCode == HttpStatusCode.SwitchingProtocols)
{
var bridgeClient = new HttpBridgeClient();
bridgeClient.host = host;
bridgeClient.ip = ip;
bridgeClient.index = index;
bridgeClient.port = port;
bridgeClient.Bridge(client);
if (i > end + 4)
{
bridgeClient.Receive(buffers, headSize, i - headSize);
}
_bridgeClients.TryAdd(bridgeClient.GetHashCode(), bridgeClient);
}
}
}
else
{
client.Disconnect(true);
client.Close();
}
}
public static String ServerChange(int states)
{
var sb = new StringBuilder();
switch (states % 3)
{
case 0:
{
var secret = UMC.Data.WebResource.Instance().Provider["appSecret"];
if (String.IsNullOrEmpty(secret))
{
sb.AppendLine("获取失败:\a应用未注册请注册应用。");
}
else
{
var webr = new Uri(APIProxy.Uri, "Transfer").WebRequest();
var ns = new System.Collections.Specialized.NameValueCollection();
UMC.Proxy.Utility.Sign(webr, secret);
try
{
var meta = JSON.Deserialize<WebMeta>(webr.Get().ReadAsString());
if (meta.ContainsKey("msg"))
{
sb.AppendLine($"获取失败:\a{meta["msg"]}。");
}
else
{
var scheme = meta["scheme"] ?? "http";
if (IsRunning)
{
sb.AppendLine($"服务状态:\b已连接");
}
else
{
sb.AppendLine($"服务状态:未连接");
}
sb.AppendLine($"Web VPN {scheme}://{meta["domain"]}/");
sb.AppendLine($"流量过期:{meta["expireTime"]}");
sb.AppendLine($"剩余流量:{meta["allowSize"]}");
sb.AppendLine($"上行流量:{meta["inputSize"]}");
sb.AppendLine($"下行流量:{meta["outputSize"]}");
sb.AppendLine();
sb.AppendLine("特别注意:\a过期后剩余流量将会清零!!!");
}
}
catch //(Exception ex)
{
sb.AppendLine($"Web VPN\a未连网");
}
}
}
break;
case 1:
if (_bridgeClients.Count > 0)
{
sb.AppendLine("正在关停Web VPN服务。");
Stop();
}
else
{
sb.AppendLine("Web VPN服务未开启。");
}
break;
case 2:
{
var secret = UMC.Data.WebResource.Instance().Provider["appSecret"];
if (String.IsNullOrEmpty(secret))
{
sb.AppendLine($"Web VPN开启失败!!!");
sb.AppendLine("失败原因:\a应用未注册请注册应用。");
}
else if (_bridgeClients.Count > 0)
{
return ServerChange(0);
}
else
{
var webr = new Uri(APIProxy.Uri, "Transfer").WebRequest();
UMC.Proxy.Utility.Sign(webr, secret);
try
{
var meta = JSON.Deserialize<WebMeta>(webr.Get().ReadAsString());
var scheme = meta["scheme"] ?? "http";
if (meta.ContainsKey("domain") == false)
{
sb.AppendLine($"Web VPN开启失败!!!");
sb.AppendLine("失败原因:\a域名未注册请注册域名。");
}
else if (meta.ContainsKey("msg"))
{
sb.AppendLine($"Web VPN开启失败!!!");
sb.AppendLine($"失败原因:\a{meta["msg"]}。");
}
else
{
var ip = meta["ip"];
var port = UMC.Data.Utility.IntParse(meta["port"], 0);
try
{
Start(meta["domain"], ip, port, 4);
var bridgeUrl = $"{scheme}://{meta["domain"]}";
sb.AppendLine($"Web VPN \b{bridgeUrl}");
sb.AppendLine($"服务状态:\a正连接");
sb.AppendLine($"剩余流量:{meta["allowSize"]}");
sb.AppendLine($"上行流量:{meta["inputSize"]}");
sb.AppendLine($"下行流量:{meta["outputSize"]}");
sb.AppendLine($"过期天数:{meta["expireTime"]}");
sb.AppendLine();
sb.AppendLine($"特别注意:\a过期后剩余流量将会清零!!!");
var provider = Data.WebResource.Instance().Provider;
if (String.Equals(provider.Attributes["bridge"], bridgeUrl) == false)
{
provider.Attributes["bridge"] = bridgeUrl;
var key = meta["key"];
var pdomain = provider["domain"];
var domain = meta["domain"];
if (String.IsNullOrEmpty(pdomain) || pdomain.StartsWith(key))
{
provider.Attributes["scheme"] = scheme;
provider.Attributes["domain"] = domain;
UMC.Proxy.WebServlet.MainDomain = domain;
}
}
provider.Attributes["webvpn"] = "true";
var pc = Reflection.Configuration("assembly") ?? new ProviderConfiguration();
pc.Add(provider);
Reflection.Configuration("assembly", pc);
}
catch (Exception ex)
{
sb.AppendLine($"Web VPN开启失败!!!");
sb.AppendLine($"失败原因:{ex.Message}");
}
}
}
catch (Exception ex)
{
sb.AppendLine($"Web VPN开启失败!!!");
sb.AppendLine($"失败原因:\a{ex.Message}");
}
}
}
break;
}
return sb.ToString();
}
public static bool IsRunning => _bridgeClients.Count > 0;
public static void Stop()
{
lock (_bridgeClients)
{
for (var i = 0; i < _bridgeClients.Count; i++)
{
try
{
_bridgeClients[i]?._Stop();
}
catch
{
}
}
_bridgeClients.Clear();
}
var provider = Data.WebResource.Instance().Provider;
provider.Attributes["webvpn"] = "false";
var pc = Reflection.Configuration("assembly") ?? new ProviderConfiguration();
pc.Add(provider);
Reflection.Configuration("assembly", pc);
}
public override void Close()
{
base.Close();
_bridgeClients.TryRemove(this.GetHashCode(), out var _);
if (_stoped == false)
{
HttpMimeServier.Register(10, this);
}
}
bool _stoped;
void _Stop()
{
_stoped = true;
base.Close();
}
HttpMimeRequest Bridge(int pid)
{
return new HttpBridgeRequest(new HttpBridgeMime(pid, this));
}
public bool Remove(int pid)
{
HttpMimeRequest httpMime;
return this.Clients.TryRemove(pid, out httpMime);
}
ConcurrentDictionary<int, HttpMimeRequest> Clients = new ConcurrentDictionary<int, HttpMimeRequest>();
protected override void Read(int pid, byte[] buffer, int index, int length)
{
if (length > 0)
{
HttpMimeRequest proxy = this.Clients.GetOrAdd(pid, this.Bridge);
try
{
proxy.Receive(buffer, index, length);
}
catch (Exception ex)
{
Utility.Error("Bridge", ex.ToString());
}
finally
{
if (proxy.IsHttpFormatError)
{
this.Clients.TryRemove(pid, out proxy);
this.Write(pid, Array.Empty<byte>(), 0, 0);
}
else if (proxy.IsWebSocket == false && proxy.IsMimeFinish)
{
this.Clients.TryRemove(pid, out proxy);
}
}
}
else
{
HttpMimeRequest mimeBody;
if (this.Clients.TryRemove(pid, out mimeBody))
{
mimeBody.ReceiveException(new Exception("穿透传输中止"));
}
}
}
void Host.IDoWorker.DoWork()
{
try
{
Connect(host, ip, port, this.index, false);
}
catch
{
HttpMimeServier.Register(30, this);
}
}
}
}