Add support for using TOML as configuration file format

Add basic support for trojan-grpc nodes.
Refactor codes.
This commit is contained in:
Tindy X committed 2021-09-20 17:02:13 +08:00
1 parent bc0a8dc925
commit 9797197c85
35 files changed
+2345 -531

No files matched your search

+361
View File
@@ -0,0 +1,361 @@
#ifndef BINDING_H_INCLUDED
#define BINDING_H_INCLUDED
#include <toml.hpp>
#include "crontask.h"
#include "proxygroup.h"
#include "regmatch.h"
#include "ruleset.h"
namespace toml
{
template<>
struct from<ProxyGroupConfig>
{
static ProxyGroupConfig from_toml(const value& v)
{
ProxyGroupConfig conf;
conf.Name = toml::find<String>(v, "name");
String type = toml::find<String>(v, "type");
String strategy = toml::find_or<String>(v, "strategy", "");
switch(hash_(type))
{
case "select"_hash:
conf.Type = ProxyGroupType::Select;
break;
case "url-test"_hash:
conf.Type = ProxyGroupType::URLTest;
conf.Url = toml::find<String>(v, "url");
conf.Interval = toml::find<Integer>(v, "interval");
conf.Tolerance = toml::find_or<Integer>(v, "tolerance", 0);
if(v.contains("lazy"))
conf.Lazy = toml::find_or<bool>(v, "lazy", false);
break;
case "load-balance"_hash:
conf.Type = ProxyGroupType::LoadBalance;
conf.Url = toml::find<String>(v, "url");
conf.Interval = toml::find<Integer>(v, "interval");
switch(hash_(strategy))
{
case "consistent-hashing"_hash:
conf.Strategy = BalanceStrategy::ConsistentHashing;
break;
case "round-robin"_hash:
conf.Strategy = BalanceStrategy::RoundRobin;
break;
}
break;
case "fallback"_hash:
conf.Type = ProxyGroupType::Fallback;
conf.Url = toml::find<String>(v, "url");
conf.Interval = toml::find<Integer>(v, "interval");
break;
case "relay"_hash:
conf.Type = ProxyGroupType::Relay;
break;
case "ssid"_hash:
conf.Type = ProxyGroupType::SSID;
break;
default:
throw toml::syntax_error("Proxy Group has incorrect type, should be one of following:\n select, url-test, load-balance, fallback, relay, ssid", v.at("type").location());
}
conf.Timeout = toml::find_or(v, "timeout", 5);
conf.Proxies = toml::find_or<StrArray>(v, "rule", {});
conf.UsingProvider = toml::find_or<StrArray>(v, "use", {});
if(conf.Proxies.empty() && conf.UsingProvider.empty())
throw toml::syntax_error("Proxy Group must contains at least one of proxy match rule or provider", v.location());
if(v.contains("disable-udp"))
conf.DisableUdp = toml::find_or(v, "disable-udp", conf.DisableUdp.get());
return conf;
}
};
template<>
struct from<RulesetConfig>
{
static RulesetConfig from_toml(const value& v)
{
RulesetConfig conf;
conf.Group = toml::find<String>(v, "group");
String type = toml::find_or<String>(v, "type", "surge-ruleset");
switch(hash_(type))
{
/*
case "surge-ruleset"_hash:
conf.Type = RulesetType::SurgeRuleset;
conf.Url = "surge:";
break;
case "quantumultx"_hash:
conf.Type = RulesetType::QuantumultX;
conf.Url = "quanx:";
break;
case "clash-domain"_hash:
conf.Type = RulesetType::ClashDomain;
conf.Url = type;
break;
case "clash-ipcidr"_hash:
conf.Type = RulesetType::ClashIpCidr;
conf.Url = type;
break;
case "clash-classic"_hash:
conf.Type = RulesetType::ClashClassic;
conf.Url = type;
break;
*/
case "surge-ruleset"_hash:
conf.Url = "surge:";
break;
case "quantumultx"_hash:
conf.Url = "quanx:";
break;
case "clash-domain"_hash:
case "clash-ipcidr"_hash:
case "clash-classic"_hash:
conf.Url = type;
break;
default:
throw toml::syntax_error("Ruleset has incorrect type, should be one of following:\n surge-ruleset, quantumultx, clash-domain, clash-ipcidr, clash-classic", v.at("type").location());
}
conf.Url += toml::find<String>(v, "ruleset");
conf.Interval = toml::find_or<Integer>(v, "interval", 86400);
return conf;
}
};
template<>
struct from<RegexMatchConfig>
{
static RegexMatchConfig from_toml(const value& v)
{
RegexMatchConfig conf;
if(v.contains("script"))
{
conf.Script = toml::find<String>(v, "script");
return conf;
}
conf.Match = toml::find<String>(v, "match");
if(v.contains("emoji"))
conf.Replace = toml::find<String>(v, "emoji");
else
conf.Replace = toml::find<String>(v, "replace");
return conf;
}
};
template<>
struct from<CronTaskConfig>
{
static CronTaskConfig from_toml(const value& v)
{
CronTaskConfig conf;
conf.Name = toml::find<String>(v, "name");
conf.CronExp = toml::find<String>(v, "cronexp");
conf.Path = toml::find<String>(v, "path");
conf.Timeout = toml::find_or<Integer>(v, "timeout", 0);
return conf;
}
};
template<>
struct from<tribool>
{
static tribool from_toml(const value& v)
{
tribool t;
t.set(v.as_boolean());
return t;
}
};
}
namespace INIBinding
{
void parseGroupTimes(const std::string &src, int *interval, int *tolerance, int *timeout)
{
std::vector<int*> ptrs;
ptrs.push_back(interval);
ptrs.push_back(timeout);
ptrs.push_back(tolerance);
string_size bpos = 0, epos = src.find(",");
for(int *x : ptrs)
{
if(x != NULL)
*x = to_int(src.substr(bpos, epos - bpos), 0);
if(epos != src.npos)
{
bpos = epos + 1;
epos = src.find(",", bpos);
}
else
return;
}
return;
}
template<class T> struct from
{};
template<>
struct from<ProxyGroupConfig>
{
static ProxyGroupConfigs from_ini(const StrArray &arr)
{
ProxyGroupConfigs confs;
for(const String &x : arr)
{
unsigned int rules_upper_bound = 0;
ProxyGroupConfig conf;
StrArray vArray = split(x, "`");
if(vArray.size() < 3)
continue;
conf.Name = vArray[0];
String type = vArray[1];
rules_upper_bound = vArray.size();
switch(hash_(type))
{
case "select"_hash:
conf.Type = ProxyGroupType::Select;
break;
case "relay"_hash:
conf.Type = ProxyGroupType::Relay;
break;
case "url-test"_hash:
conf.Type = ProxyGroupType::URLTest;
break;
case "fallback"_hash:
conf.Type = ProxyGroupType::Fallback;
break;
case "load-balance"_hash:
conf.Type = ProxyGroupType::LoadBalance;
break;
case "ssid"_hash:
conf.Type = ProxyGroupType::SSID;
break;
default:
continue;
}
if(conf.Type == ProxyGroupType::URLTest || conf.Type == ProxyGroupType::LoadBalance || conf.Type == ProxyGroupType::Fallback)
{
if(rules_upper_bound < 5)
continue;
rules_upper_bound -= 2;
conf.Url = vArray[rules_upper_bound];
parseGroupTimes(vArray[rules_upper_bound + 1], &conf.Interval, &conf.Tolerance, &conf.Timeout);
}
for(unsigned int i = 2; i < rules_upper_bound; i++)
{
if(startsWith(vArray[i], "!!PROVIDER="))
{
string_array list = split(vArray[i].substr(11), ",");
conf.UsingProvider.reserve(conf.UsingProvider.size() + list.size());
std::move(list.begin(), list.end(), std::back_inserter(conf.UsingProvider));
}
else
conf.Proxies.emplace_back(std::move(vArray[i]));
}
confs.emplace_back(std::move(conf));
}
return confs;
}
};
template<>
struct from<RulesetConfig>
{
static RulesetConfigs from_ini(const StrArray &arr)
{
/*
static const std::map<std::string, RulesetType> RulesetTypes = {
{"clash-domain:", RulesetType::ClashDomain},
{"clash-ipcidr:", RulesetType::ClashIpCidr},
{"clash-classic:", RulesetType::ClashClassic},
{"quanx:", RulesetType::QuantumultX},
{"surge:", RulesetType::SurgeRuleset}
};
*/
RulesetConfigs confs;
for(const String &x : arr)
{
RulesetConfig conf;
String::size_type pos = x.find(",");
if(pos == String::npos)
continue;
conf.Group = x.substr(0, pos);
if(x.substr(pos + 1, 2) == "[]")
{
conf.Url = x.substr(pos + 1);
//conf.Type = RulesetType::SurgeRuleset;
confs.emplace_back(std::move(conf));
continue;
}
String::size_type epos = x.rfind(",");
if(pos != epos)
{
conf.Interval = to_int(x.substr(epos + 1), 0);
conf.Url = x.substr(pos + 1, epos - pos - 1);
}
else
conf.Url = x.substr(pos + 1);
confs.emplace_back(std::move(conf));
}
return confs;
}
};
template<>
struct from<CronTaskConfig>
{
static CronTaskConfigs from_ini(const StrArray &arr)
{
CronTaskConfigs confs;
for(const String &x : arr)
{
CronTaskConfig conf;
StrArray vArray = split(x, "`");
if(vArray.size() < 3)
continue;
conf.Name = vArray[0];
conf.CronExp = vArray[1];
conf.Path = vArray[2];
if(vArray.size() > 3)
conf.Timeout = to_int(vArray[3], 0);
confs.emplace_back(std::move(conf));
}
return confs;
}
};
template<>
struct from<RegexMatchConfig>
{
static RegexMatchConfigs from_ini(const StrArray &arr, const std::string &delimiter)
{
RegexMatchConfigs confs;
for(const String &x : arr)
{
RegexMatchConfig conf;
if(startsWith(x, "script:"))
{
conf.Script = x.substr(7);
confs.emplace_back(std::move(conf));
continue;
}
String::size_type pos = x.rfind(delimiter);
conf.Match = x.substr(0, pos);
if(pos != String::npos && pos < x.size() - 1)
conf.Replace = x.substr(pos + 1);
confs.emplace_back(std::move(conf));
}
return confs;
}
};
}
#endif // BINDING_H_INCLUDED
+16
View File
@@ -0,0 +1,16 @@
#ifndef CRONTASK_H_INCLUDED
#define CRONTASK_H_INCLUDED
#include "def.h"
struct CronTaskConfig
{
String Name;
String CronExp;
String Path;
Integer Timeout = 0;
};
using CronTaskConfigs = std::vector<CronTaskConfig>;
#endif // CRONTASK_H_INCLUDED
+16
View File
@@ -0,0 +1,16 @@
#ifndef DEF_H_INCLUDED
#define DEF_H_INCLUDED
#include <string>
#include <vector>
#include <tuple>
#include "../utils/string.h"
#include "../utils/tribool.h"
using String = std::string;
using Integer = int32_t;
using StrArray = string_array;
using Boolean = tribool;
#endif // DEF_H_INCLUDED
+63
View File
@@ -0,0 +1,63 @@
#ifndef PROXYGROUP_H_INCLUDED
#define PROXYGROUP_H_INCLUDED
#include "def.h"
enum ProxyGroupType
{
Select,
URLTest,
Fallback,
LoadBalance,
Relay,
SSID
};
enum BalanceStrategy
{
ConsistentHashing,
RoundRobin
};
struct ProxyGroupConfig
{
String Name;
ProxyGroupType Type;
StrArray Proxies;
StrArray UsingProvider;
String Url;
Integer Interval = 0;
Integer Timeout = 0;
Integer Tolerance = 0;
BalanceStrategy Strategy = BalanceStrategy::ConsistentHashing;
Boolean Lazy;
Boolean DisableUdp;
String TypeStr() const
{
switch(Type)
{
case ProxyGroupType::Select: return "select";
case ProxyGroupType::URLTest: return "url-test";
case ProxyGroupType::LoadBalance: return "load-balance";
case ProxyGroupType::Fallback: return "fallback";
case ProxyGroupType::Relay: return "relay";
case ProxyGroupType::SSID: return "ssid";
}
return "";
}
String StrategyStr() const
{
switch(Strategy)
{
case BalanceStrategy::ConsistentHashing: return "consistent-hashing";
case BalanceStrategy::RoundRobin: return "round-robin";
}
return "";
}
};
using ProxyGroupConfigs = std::vector<ProxyGroupConfig>;
#endif // PROXYGROUP_H_INCLUDED
+15
View File
@@ -0,0 +1,15 @@
#ifndef REGMATCH_H_INCLUDED
#define REGMATCH_H_INCLUDED
#include "def.h"
struct RegexMatchConfig
{
String Match;
String Replace;
String Script;
};
using RegexMatchConfigs = std::vector<RegexMatchConfig>;
#endif // REGMATCH_H_INCLUDED
+29
View File
@@ -0,0 +1,29 @@
#ifndef RULESET_H_INCLUDED
#define RULESET_H_INCLUDED
#include "def.h"
enum RulesetType
{
SurgeRuleset,
QuantumultX,
ClashDomain,
ClashIpCidr,
ClashClassic
};
struct RulesetConfig
{
String Group;
//RulesetType Type = RulesetType::SurgeRuleset;
String Url;
Integer Interval = 86400;
bool operator==(const RulesetConfig &r) const
{
return Group == r.Group && Url == r.Url && Interval == r.Interval;
}
};
using RulesetConfigs = std::vector<RulesetConfig>;
#endif // RULESET_H_INCLUDED
+2 -2
View File
@@ -37,8 +37,8 @@ int addNodes(std::string link, std::vector<Proxy> &allNodes, int groupID, parse_
std::string &proxy = *parse_set.proxy, &subInfo = *parse_set.sub_info;
string_array &exclude_remarks = *parse_set.exclude_remarks;
string_array &include_remarks = *parse_set.include_remarks;
string_array &stream_rules = *parse_set.stream_rules;
string_array &time_rules = *parse_set.time_rules;
RegexMatchConfigs &stream_rules = *parse_set.stream_rules;
RegexMatchConfigs &time_rules = *parse_set.time_rules;
string_icase_map &request_headers = *parse_set.request_header;
bool &authorized = parse_set.authorized;
+3 -2
View File
@@ -6,6 +6,7 @@
#include <limits.h>
#include <quickjspp.hpp>
#include "../../config/regmatch.h"
#include "../../parser/config/proxy.h"
#include "../../utils/map_extra.h"
#include "../../utils/string.h"
@@ -15,8 +16,8 @@ struct parse_settings
std::string *proxy = nullptr;
string_array *exclude_remarks = nullptr;
string_array *include_remarks = nullptr;
string_array *stream_rules = nullptr;
string_array *time_rules = nullptr;
RegexMatchConfigs *stream_rules = nullptr;
RegexMatchConfigs *time_rules = nullptr;
std::string *sub_info = nullptr;
bool authorized = false;
string_icase_map *request_header = nullptr;
+141 -238
View File
@@ -8,6 +8,7 @@
#include <rapidjson/error/en.h>
#include <yaml-cpp/yaml.h>
#include "../../config/regmatch.h"
#include "../../generator/config/subexport.h"
#include "../../generator/template/templates.h"
#include "../../parser/config/proxy.h"
@@ -63,19 +64,17 @@ std::string vmessLinkConstruct(const std::string &remarks, const std::string &ad
return sb.GetString();
}
void nodeRename(Proxy &node, const string_array &rename_array, extra_settings &ext)
void nodeRename(Proxy &node, const RegexMatchConfigs &rename_array, extra_settings &ext)
{
string_size pos;
std::string match, rep;
std::string &remark = node.Remark, original_remark = node.Remark, returned_remark, real_rule;
for(const std::string &x : rename_array)
for(const RegexMatchConfig &x : rename_array)
{
if(startsWith(x, "!!script:"))
if(!x.Script.empty())
{
script_safe_runner(ext.js_runtime, ext.js_context, [&](qjs::Context &ctx)
{
std::string script = x.substr(9);
std::string script = x.Script;
if(startsWith(script, "path:"))
script = fileGet(script.substr(5), true);
try
@@ -93,14 +92,8 @@ void nodeRename(Proxy &node, const string_array &rename_array, extra_settings &e
}, gScriptCleanContext);
continue;
}
pos = x.rfind("@");
match = x.substr(0, pos);
if(pos != x.npos && pos < x.size())
rep = x.substr(pos + 1);
else
rep.clear();
if(applyMatcher(match, real_rule, node) && real_rule.size())
remark = regReplace(remark, real_rule, rep);
if(applyMatcher(x.Match, real_rule, node) && real_rule.size())
remark = regReplace(remark, real_rule, x.Replace);
}
if(remark.empty())
remark = original_remark;
@@ -123,19 +116,18 @@ std::string removeEmoji(const std::string &orig_remark)
return remark;
}
std::string addEmoji(const Proxy &node, const string_array &emoji_array, extra_settings &ext)
std::string addEmoji(const Proxy &node, const RegexMatchConfigs &emoji_array, extra_settings &ext)
{
std::string real_rule, ret;
string_size pos;
for(const std::string &x : emoji_array)
for(const RegexMatchConfig &x : emoji_array)
{
if(startsWith(x, "!!script:"))
if(!x.Script.empty())
{
std::string result;
script_safe_runner(ext.js_runtime, ext.js_context, [&](qjs::Context &ctx)
{
std::string script = x.substr(9);
std::string script = x.Script;
if(startsWith(script, "path:"))
script = fileGet(script.substr(5), true);
try
@@ -155,11 +147,10 @@ std::string addEmoji(const Proxy &node, const string_array &emoji_array, extra_s
return result;
continue;
}
pos = x.rfind(",");
if(pos == x.npos)
if(x.Replace.empty())
continue;
if(applyMatcher(x.substr(0, pos), real_rule, node) && real_rule.size() && regFind(node.Remark, real_rule))
return x.substr(pos + 1) + " " + node.Remark;
if(applyMatcher(x.Match, real_rule, node) && real_rule.size() && regFind(node.Remark, real_rule))
return x.Replace + " " + node.Remark;
}
return node.Remark;
}
@@ -206,7 +197,7 @@ void parseGroupTimes(const std::string &src, int *interval, int *tolerance, int
return;
}
void groupGenerate(std::string &rule, std::vector<Proxy> &nodelist, string_array &filtered_nodelist, bool add_direct, extra_settings &ext)
void groupGenerate(const std::string &rule, std::vector<Proxy> &nodelist, string_array &filtered_nodelist, bool add_direct, extra_settings &ext)
{
std::string real_rule;
if(startsWith(rule, "[]") && add_direct)
@@ -291,11 +282,11 @@ void preprocessNodes(std::vector<Proxy> &nodes, extra_settings &ext)
}
}
void proxyToClash(std::vector<Proxy> &nodes, YAML::Node &yamlnode, const string_array &extra_proxy_group, bool clashR, extra_settings &ext)
void proxyToClash(std::vector<Proxy> &nodes, YAML::Node &yamlnode, const ProxyGroupConfigs &extra_proxy_group, bool clashR, extra_settings &ext)
{
YAML::Node proxies, singleproxy, singlegroup, original_groups;
std::vector<Proxy> nodelist;
string_array vArray, remarks_list, filtered_nodelist;
string_array remarks_list, filtered_nodelist;
/// proxies style
bool block = false, compact = false;
switch(hash_(ext.clash_proxies_style))
@@ -376,11 +367,11 @@ void proxyToClash(std::vector<Proxy> &nodes, YAML::Node &yamlnode, const string_
break;
case "ws"_hash:
singleproxy["network"] = x.TransferProtocol;
singleproxy["ws-path"] = x.Path;
singleproxy["ws-opts"]["path"] = x.Path;
if(x.Host.size())
singleproxy["ws-headers"]["Host"] = x.Host;
singleproxy["ws-opts"]["headers"]["Host"] = x.Host;
if(x.Edge.size())
singleproxy["ws-headers"]["Edge"] = x.Edge;
singleproxy["ws-opts"]["headers"]["Edge"] = x.Edge;
break;
case "http"_hash:
singleproxy["network"] = x.TransferProtocol;
@@ -472,6 +463,16 @@ void proxyToClash(std::vector<Proxy> &nodes, YAML::Node &yamlnode, const string_
singleproxy["password"].SetTag("str");
if(!scv.is_undef())
singleproxy["skip-cert-verify"] = scv.get();
switch(hash_(x.TransferProtocol))
{
case "tcp"_hash:
break;
case "grpc"_hash:
singleproxy["network"] = x.TransferProtocol;
if(!x.Path.empty())
singleproxy["grpc-opts"]["grpc-service-name"] = x.Path;
break;
}
break;
case ProxyType::Snell:
singleproxy["type"] = "snell";
@@ -516,60 +517,45 @@ void proxyToClash(std::vector<Proxy> &nodes, YAML::Node &yamlnode, const string_
else
yamlnode["Proxy"] = proxies;
string_array providers;
for(const std::string &x : extra_proxy_group)
for(const ProxyGroupConfig &x : extra_proxy_group)
{
singlegroup.reset();
eraseElements(filtered_nodelist);
eraseElements(providers);
unsigned int rules_upper_bound = 0;
vArray = split(x, "`");
if(vArray.size() < 3)
continue;
singlegroup["name"] = x.Name;
singlegroup["type"] = x.TypeStr();
singlegroup["name"] = vArray[0];
singlegroup["type"] = vArray[1];
int interval = 0, tolerance = 0;
rules_upper_bound = vArray.size();
switch(hash_(vArray[1]))
switch(x.Type)
{
case "select"_hash:
case "relay"_hash:
case ProxyGroupType::Select:
case ProxyGroupType::Relay:
break;
case "url-test"_hash:
case "fallback"_hash:
case "load-balance"_hash:
if(rules_upper_bound < 5)
continue;
rules_upper_bound -= 2;
singlegroup["url"] = vArray[rules_upper_bound];
parseGroupTimes(vArray[rules_upper_bound + 1], &interval, &tolerance, NULL);
if(interval)
singlegroup["interval"] = interval;
if(tolerance)
singlegroup["tolerance"] = tolerance;
case ProxyGroupType::LoadBalance:
singlegroup["strategy"] = x.StrategyStr();
[[fallthrough]];
case ProxyGroupType::URLTest:
if(!x.Lazy.is_undef())
singlegroup["lazy"] = x.Lazy.get();
[[fallthrough]];
case ProxyGroupType::Fallback:
singlegroup["url"] = x.Url;
if(x.Interval > 0)
singlegroup["interval"] = x.Interval;
if(x.Tolerance > 0)
singlegroup["tolerance"] = x.Tolerance;
break;
default:
continue;
}
if(!x.DisableUdp.is_undef())
singlegroup["disable-udp"] = x.DisableUdp.get();
for(unsigned int i = 2; i < rules_upper_bound; i++)
{
if(startsWith(vArray[i], "!!PROVIDER="))
{
string_array list = split(vArray[i].substr(11), ",");
providers.reserve(providers.size() + list.size());
std::move(list.begin(), list.end(), std::back_inserter(providers));
}
else
groupGenerate(vArray[i], nodelist, filtered_nodelist, true, ext);
}
for(const auto& y : x.Proxies)
groupGenerate(y, nodelist, filtered_nodelist, true, ext);
if(providers.size())
singlegroup["use"] = providers;
if(x.UsingProvider.size())
singlegroup["use"] = x.UsingProvider;
else
{
if(filtered_nodelist.empty())
@@ -582,7 +568,7 @@ void proxyToClash(std::vector<Proxy> &nodes, YAML::Node &yamlnode, const string_
bool replace_flag = false;
for(unsigned int i = 0; i < original_groups.size(); i++)
{
if(original_groups[i]["name"].as<std::string>() == vArray[0])
if(original_groups[i]["name"].as<std::string>() == x.Name)
{
original_groups[i] = singlegroup;
replace_flag = true;
@@ -599,7 +585,7 @@ void proxyToClash(std::vector<Proxy> &nodes, YAML::Node &yamlnode, const string_
yamlnode["Proxy Group"] = original_groups;
}
std::string proxyToClash(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, bool clashR, extra_settings &ext)
std::string proxyToClash(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, bool clashR, extra_settings &ext)
{
YAML::Node yamlnode;
@@ -647,7 +633,7 @@ std::string proxyToClash(std::vector<Proxy> &nodes, const std::string &base_conf
return output_content;
}
std::string proxyToSurge(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, int surge_ver, extra_settings &ext)
std::string proxyToSurge(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, int surge_ver, extra_settings &ext)
{
INIReader ini;
std::string proxy;
@@ -656,7 +642,7 @@ std::string proxyToSurge(std::vector<Proxy> &nodes, const std::string &base_conf
std::vector<Proxy> nodelist;
unsigned short local_port = 1080;
string_array vArray, remarks_list, filtered_nodelist, args;
string_array remarks_list, filtered_nodelist, args;
ini.store_any_line = true;
// filter out sections that requires direct-save
@@ -833,53 +819,35 @@ std::string proxyToSurge(std::vector<Proxy> &nodes, const std::string &base_conf
ini.SetCurrentSection("Proxy Group");
ini.EraseSection();
for(const std::string &x : extra_proxy_group)
for(const ProxyGroupConfig &x : extra_proxy_group)
{
//group pref
std::string url;
int interval = 0, tolerance = 0, timeout = 0;
eraseElements(filtered_nodelist);
unsigned int rules_upper_bound = 0;
url.clear();
proxy.clear();
vArray = split(x, "`");
if(vArray.size() < 3)
continue;
rules_upper_bound = vArray.size();
switch(hash_(vArray[1]))
switch(x.Type)
{
case "select"_hash:
case ProxyGroupType::Select:
case ProxyGroupType::URLTest:
case ProxyGroupType::Fallback:
break;
case "load-balance"_hash:
case ProxyGroupType::LoadBalance:
if(surge_ver < 1)
continue;
[[fallthrough]];
case "url-test"_hash:
case "fallback"_hash:
if(rules_upper_bound < 5)
continue;
rules_upper_bound -= 2;
url = vArray[rules_upper_bound];
parseGroupTimes(vArray[rules_upper_bound + 1], &interval, &tolerance, &timeout);
break;
case "ssid"_hash:
if(rules_upper_bound < 4)
continue;
proxy = vArray[1] + ",default=" + vArray[2] + ",";
proxy += std::accumulate(vArray.begin() + 4, vArray.end(), vArray[3], [](std::string a, std::string b)
case ProxyGroupType::SSID:
proxy = x.TypeStr() + ",default=" + x.Proxies[0] + ",";
proxy += std::accumulate(x.Proxies.begin() + 2, x.Proxies.end(), x.Proxies[1], [](std::string a, std::string b)
{
return std::move(a) + "," + std::move(b);
});
ini.Set("{NONAME}", vArray[0] + " = " + proxy); //insert order
ini.Set("{NONAME}", x.Name + " = " + proxy); //insert order
continue;
default:
continue;
}
for(unsigned int i = 2; i < rules_upper_bound; i++)
groupGenerate(vArray[i], nodelist, filtered_nodelist, true, ext);
for(const auto &y : x.Proxies)
groupGenerate(y, nodelist, filtered_nodelist, true, ext);
if(!filtered_nodelist.size())
filtered_nodelist.emplace_back("DIRECT");
@@ -892,28 +860,28 @@ std::string proxyToSurge(std::vector<Proxy> &nodes, const std::string &base_conf
case "direct"_hash:
case "reject"_hash:
case "reject-tinygif"_hash:
ini.Set("Proxy", "{NONAME}", vArray[0] + " = " + proxy);
ini.Set("Proxy", "{NONAME}", x.Name + " = " + proxy);
continue;
}
}
proxy = vArray[1] + ",";
proxy = x.TypeStr() + ",";
proxy += std::accumulate(std::next(filtered_nodelist.cbegin()), filtered_nodelist.cend(), filtered_nodelist[0], [](std::string a, std::string b)
{
return std::move(a) + "," + std::move(b);
});
if(vArray[1] == "url-test" || vArray[1] == "fallback")
if(x.Type == ProxyGroupType::URLTest || x.Type == ProxyGroupType::Fallback)
{
proxy += ",url=" + url + ",interval=" + std::to_string(interval);
if(tolerance > 0)
proxy += ",tolerance=" + std::to_string(tolerance);
if(timeout > 0)
proxy += ",timeout=" + std::to_string(timeout);
proxy += ",url=" + x.Url + ",interval=" + std::to_string(x.Interval);
if(x.Tolerance > 0)
proxy += ",tolerance=" + std::to_string(x.Tolerance);
if(x.Timeout > 0)
proxy += ",timeout=" + std::to_string(x.Timeout);
}
else if(vArray[1] == "load-balance")
proxy += ",url=" + url;
else if(x.Type == ProxyGroupType::LoadBalance)
proxy += ",url=" + x.Url;
ini.Set("{NONAME}", vArray[0] + " = " + proxy); //insert order
ini.Set("{NONAME}", x.Name + " = " + proxy); //insert order
}
if(ext.enable_rule_generator)
@@ -1075,7 +1043,7 @@ std::string proxyToSSSub(std::string base_conf, std::vector<Proxy> &nodes, extra
return output_content;
}
std::string proxyToQuan(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext)
std::string proxyToQuan(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext)
{
INIReader ini;
ini.store_any_line = true;
@@ -1102,7 +1070,7 @@ std::string proxyToQuan(std::vector<Proxy> &nodes, const std::string &base_conf,
return ini.ToString();
}
void proxyToQuan(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext)
void proxyToQuan(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext)
{
std::string type, proxyStr;
tribool scv;
@@ -1244,47 +1212,27 @@ void proxyToQuan(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_
std::string singlegroup;
std::string name, proxies;
string_array vArray;
for(const std::string &x : extra_proxy_group)
for(const ProxyGroupConfig &x : extra_proxy_group)
{
eraseElements(filtered_nodelist);
unsigned int rules_upper_bound = 0;
vArray = split(x, "`");
if(vArray.size() < 3)
continue;
rules_upper_bound = vArray.size();
switch(hash_(vArray[1]))
switch(x.Type)
{
case "select"_hash:
case ProxyGroupType::Select:
case ProxyGroupType::Fallback:
type = "static";
break;
case "fallback"_hash:
type = "static";
if(vArray.size() < 5)
continue;
rules_upper_bound -= 2;
break;
case "url-test"_hash:
case ProxyGroupType::URLTest:
type = "auto";
if(vArray.size() < 5)
continue;
rules_upper_bound -= 2;
break;
case "load-balance"_hash:
case ProxyGroupType::LoadBalance:
type = "balance, round-robin";
if(vArray.size() < 5)
continue;
rules_upper_bound -= 2;
break;
case "ssid"_hash:
case ProxyGroupType::SSID:
{
if(rules_upper_bound < 4)
continue;
singlegroup = vArray[0] + " : wifi = " + vArray[2];
singlegroup = x.Name + " : wifi = " + x.Proxies[0];
std::string content, celluar, celluar_matcher = R"(^(.*?),?celluar\s?=\s?(.*?)(,.*)$)", rem_a, rem_b;
for(auto iter = vArray.begin() + 3; iter != vArray.end(); iter++)
for(auto iter = x.Proxies.begin() + 1; iter != x.Proxies.end(); iter++)
{
if(regGetMatch(*iter, celluar_matcher, 4, 0, &rem_a, &celluar, &rem_b))
{
@@ -1303,10 +1251,8 @@ void proxyToQuan(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_
continue;
}
name = vArray[0];
for(unsigned int i = 2; i < rules_upper_bound; i++)
groupGenerate(vArray[i], nodelist, filtered_nodelist, true, ext);
for(const auto &y : x.Proxies)
groupGenerate(y, nodelist, filtered_nodelist, true, ext);
if(!filtered_nodelist.size())
filtered_nodelist.emplace_back("direct");
@@ -1319,7 +1265,7 @@ void proxyToQuan(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_
return std::move(a) + "\n" + std::move(b);
});
singlegroup = name + " : " + type;
singlegroup = x.Name + " : " + type;
if(type == "static")
singlegroup += ", " + filtered_nodelist[0];
singlegroup += "\n" + proxies + "\n";
@@ -1330,7 +1276,7 @@ void proxyToQuan(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_
rulesetToSurge(ini, ruleset_content_array, -2, ext.overwrite_original_rules, std::string());
}
std::string proxyToQuanX(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext)
std::string proxyToQuanX(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext)
{
INIReader ini;
ini.store_any_line = true;
@@ -1364,7 +1310,7 @@ std::string proxyToQuanX(std::vector<Proxy> &nodes, const std::string &base_conf
return ini.ToString();
}
void proxyToQuanX(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext)
void proxyToQuanX(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext)
{
std::string type;
std::string remark, hostname, port, method;
@@ -1503,53 +1449,37 @@ void proxyToQuanX(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset
ini.EraseSection();
std::string singlegroup;
std::string name, proxies;
std::string proxies;
string_array vArray;
for(const std::string &x : extra_proxy_group)
for(const ProxyGroupConfig &x : extra_proxy_group)
{
eraseElements(filtered_nodelist);
unsigned int rules_upper_bound = 0;
vArray = split(x, "`");
if(vArray.size() < 3)
continue;
rules_upper_bound = vArray.size();
switch(hash_(vArray[1]))
switch(x.Type)
{
case "select"_hash:
case ProxyGroupType::Select:
type = "static";
break;
case "url-test"_hash:
case "fallback"_hash:
case ProxyGroupType::URLTest:
case ProxyGroupType::Fallback:
type = "available";
if(rules_upper_bound < 5)
continue;
rules_upper_bound -= 2;
break;
case "load-balance"_hash:
case ProxyGroupType::LoadBalance:
type = "round-robin";
if(rules_upper_bound < 5)
continue;
rules_upper_bound -= 2;
break;
case "ssid"_hash:
if(rules_upper_bound < 4)
continue;
case ProxyGroupType::SSID:
type = "ssid";
for(auto iter = vArray.begin() + 2; iter != vArray.end(); iter++)
for(auto iter = x.Proxies.begin(); iter != x.Proxies.end(); iter++)
filtered_nodelist.emplace_back(replaceAllDistinct(*iter, "=", ":"));
break;
default:
continue;
}
name = vArray[0];
if(hash_(vArray[1]) != "ssid"_hash)
if(x.Type != ProxyGroupType::SSID)
{
for(unsigned int i = 2; i < rules_upper_bound; i++)
groupGenerate(vArray[i], nodelist, filtered_nodelist, true, ext);
for(const auto &y : x.Proxies)
groupGenerate(y, nodelist, filtered_nodelist, true, ext);
if(!filtered_nodelist.size())
filtered_nodelist.emplace_back("direct");
@@ -1558,12 +1488,12 @@ void proxyToQuanX(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset
type = "static";
}
auto iter = std::find_if(original_groups.begin(), original_groups.end(), [name](const string_multimap::value_type &n)
auto iter = std::find_if(original_groups.begin(), original_groups.end(), [&](const string_multimap::value_type &n)
{
std::string groupdata = n.second;
std::string::size_type cpos = groupdata.find(",");
if(cpos != groupdata.npos)
return trim(groupdata.substr(0, cpos)) == name;
return trim(groupdata.substr(0, cpos)) == x.Name;
else
return false;
});
@@ -1582,7 +1512,7 @@ void proxyToQuanX(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset
return std::move(a) + ", " + std::move(b);
});
singlegroup = type + "=" + name + ", " + proxies;
singlegroup = type + "=" + x.Name + ", " + proxies;
ini.Set("{NONAME}", singlegroup);
}
@@ -1745,7 +1675,7 @@ std::string proxyToSSD(std::vector<Proxy> &nodes, std::string &group, std::strin
return "ssd://" + base64Encode(sb.GetString());
}
std::string proxyToMellow(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext)
std::string proxyToMellow(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext)
{
INIReader ini;
ini.store_any_line = true;
@@ -1760,7 +1690,7 @@ std::string proxyToMellow(std::vector<Proxy> &nodes, const std::string &base_con
return ini.ToString();
}
void proxyToMellow(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext)
void proxyToMellow(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext)
{
std::string proxy;
std::string type, remark, hostname, port, username, password, method;
@@ -1849,36 +1779,25 @@ void proxyToMellow(std::vector<Proxy> &nodes, INIReader &ini, std::vector<rulese
ini.SetCurrentSection("EndpointGroup");
for(const std::string &x : extra_proxy_group)
for(const ProxyGroupConfig &x : extra_proxy_group)
{
eraseElements(filtered_nodelist);
unsigned int rules_upper_bound = 0;
url.clear();
proxy.clear();
vArray = split(x, "`");
if(vArray.size() < 3)
continue;
rules_upper_bound = vArray.size();
switch(hash_(vArray[1]))
switch(x.Type)
{
case "select"_hash:
break;
case "url-test"_hash:
case "fallback"_hash:
case "load-balance"_hash:
if(vArray.size() < 5)
continue;
rules_upper_bound -= 2;
url = vArray[vArray.size() - 2];
case ProxyGroupType::Select:
case ProxyGroupType::URLTest:
case ProxyGroupType::Fallback:
case ProxyGroupType::LoadBalance:
break;
default:
continue;
}
for(unsigned int i = 2; i < rules_upper_bound; i++)
groupGenerate(vArray[i], nodelist, filtered_nodelist, false, ext);
for(const auto &y : x.Proxies)
groupGenerate(y, nodelist, filtered_nodelist, false, ext);
if(!filtered_nodelist.size())
{
@@ -1897,7 +1816,7 @@ void proxyToMellow(std::vector<Proxy> &nodes, INIReader &ini, std::vector<rulese
proxy += ",url=" + url;
*/
proxy = vArray[0] + ", ";
proxy = x.Name + ", ";
/*
for(std::string &y : filtered_nodelist)
proxy += y + ":";
@@ -1916,7 +1835,7 @@ void proxyToMellow(std::vector<Proxy> &nodes, INIReader &ini, std::vector<rulese
rulesetToSurge(ini, ruleset_content_array, 0, ext.overwrite_original_rules, std::string());
}
std::string proxyToLoon(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext)
std::string proxyToLoon(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext)
{
rapidjson::Document json;
INIReader ini;
@@ -1924,9 +1843,6 @@ std::string proxyToLoon(std::vector<Proxy> &nodes, const std::string &base_conf,
std::string output_nodelist;
tribool scv;
std::vector<Proxy> nodelist;
//group pref
std::string url;
int interval = 0;
string_array vArray, remarks_list, filtered_nodelist;
@@ -2039,51 +1955,38 @@ std::string proxyToLoon(std::vector<Proxy> &nodes, const std::string &base_conf,
ini.SetCurrentSection("Proxy Group");
ini.EraseSection();
for(const std::string &x : extra_proxy_group)
for(const ProxyGroupConfig &x : extra_proxy_group)
{
eraseElements(filtered_nodelist);
unsigned int rules_upper_bound = 0;
url.clear();
proxy.clear();
vArray = split(x, "`");
if(vArray.size() < 3)
continue;
rules_upper_bound = vArray.size();
switch(hash_(vArray[1]))
switch(x.Type)
{
case "select"_hash:
case ProxyGroupType::Select:
case ProxyGroupType::URLTest:
case ProxyGroupType::Fallback:
break;
case "url-test"_hash:
case "fallback"_hash:
if(vArray.size() < 5)
case ProxyGroupType::SSID:
if(x.Proxies.size() < 2)
continue;
rules_upper_bound -= 2;
url = vArray[rules_upper_bound];
parseGroupTimes(vArray[rules_upper_bound + 1], &interval, NULL, NULL);
break;
case "ssid"_hash:
if(vArray.size() < 4)
continue;
proxy = vArray[1] + ",default=" + vArray[2] + ",";
proxy += std::accumulate(vArray.begin() + 4, vArray.end(), vArray[3], [](std::string a, std::string b)
proxy = x.TypeStr() + ",default=" + x.Proxies[0] + ",";
proxy += std::accumulate(x.Proxies.begin() + 2, x.Proxies.end(), vArray[1], [](std::string a, std::string b)
{
return std::move(a) + "," + std::move(b);
});
ini.Set("{NONAME}", vArray[0] + " = " + proxy); //insert order
ini.Set("{NONAME}", x.Name + " = " + proxy); //insert order
continue;
default:
continue;
}
for(unsigned int i = 2; i < rules_upper_bound; i++)
groupGenerate(vArray[i], nodelist, filtered_nodelist, true, ext);
for(const auto &y : x.Proxies)
groupGenerate(y, nodelist, filtered_nodelist, true, ext);
if(!filtered_nodelist.size())
filtered_nodelist.emplace_back("DIRECT");
proxy = vArray[1] + ",";
proxy = x.TypeStr() + ",";
/*
for(std::string &y : filtered_nodelist)
proxy += "," + y;
@@ -2092,10 +1995,10 @@ std::string proxyToLoon(std::vector<Proxy> &nodes, const std::string &base_conf,
{
return std::move(a) + "," + std::move(b);
});
if(vArray[1] == "url-test" || vArray[1] == "fallback")
proxy += ",url=" + url + ",interval=" + std::to_string(interval);
if(x.Type == ProxyGroupType::URLTest || x.Type == ProxyGroupType::Fallback)
proxy += ",url=" + x.Url + ",interval=" + std::to_string(x.Interval);
ini.Set("{NONAME}", vArray[0] + " = " + proxy); //insert order
ini.Set("{NONAME}", x.Name + " = " + proxy); //insert order
}
if(ext.enable_rule_generator)
+14 -12
View File
@@ -5,6 +5,8 @@
#include <quickjspp.hpp>
#include "../../config/proxygroup.h"
#include "../../config/regmatch.h"
#include "../../parser/config/proxy.h"
#include "../../utils/ini_reader/ini_reader.h"
#include "../../utils/string.h"
@@ -15,8 +17,8 @@ struct extra_settings
{
bool enable_rule_generator = true;
bool overwrite_original_rules = true;
string_array rename_array;
string_array emoji_array;
RegexMatchConfigs rename_array;
RegexMatchConfigs emoji_array;
bool add_emoji = false;
bool remove_emoji = false;
bool append_proxy_type = false;
@@ -52,18 +54,18 @@ void rulesetToClash(YAML::Node &base_rule, std::vector<ruleset_content> &ruleset
void rulesetToSurge(INIReader &base_rule, std::vector<ruleset_content> &ruleset_content_array, int surge_ver, bool overwrite_original_rules, std::string remote_path_prefix);
void preprocessNodes(std::vector<Proxy> &nodes, extra_settings &ext);
std::string proxyToClash(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, bool clashR, extra_settings &ext);
void proxyToClash(std::vector<Proxy> &nodes, YAML::Node &yamlnode, const string_array &extra_proxy_group, bool clashR, extra_settings &ext);
std::string proxyToSurge(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, int surge_ver, extra_settings &ext);
std::string proxyToMellow(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext);
void proxyToMellow(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext);
std::string proxyToLoon(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext);
std::string proxyToClash(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, bool clashR, extra_settings &ext);
void proxyToClash(std::vector<Proxy> &nodes, YAML::Node &yamlnode, const ProxyGroupConfigs &extra_proxy_group, bool clashR, extra_settings &ext);
std::string proxyToSurge(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, int surge_ver, extra_settings &ext);
std::string proxyToMellow(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext);
void proxyToMellow(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext);
std::string proxyToLoon(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext);
std::string proxyToSSSub(std::string base_conf, std::vector<Proxy> &nodes, extra_settings &ext);
std::string proxyToSingle(std::vector<Proxy> &nodes, int types, extra_settings &ext);
std::string proxyToQuanX(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext);
void proxyToQuanX(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext);
std::string proxyToQuan(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext);
void proxyToQuan(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, const string_array &extra_proxy_group, extra_settings &ext);
std::string proxyToQuanX(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext);
void proxyToQuanX(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext);
std::string proxyToQuan(std::vector<Proxy> &nodes, const std::string &base_conf, std::vector<ruleset_content> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext);
void proxyToQuan(std::vector<Proxy> &nodes, INIReader &ini, std::vector<ruleset_content> &ruleset_content_array, const ProxyGroupConfigs &extra_proxy_group, extra_settings &ext);
std::string proxyToSSD(std::vector<Proxy> &nodes, std::string &group, std::string &userinfo, extra_settings &ext);
#endif // SUBEXPORT_H_INCLUDED
+475 -97
View File
@@ -6,6 +6,7 @@
#include <inja.hpp>
#include <yaml-cpp/yaml.h>
#include "../config/binding.h"
#include "../generator/config/ruleconvert.h"
#include "../generator/config/nodemanip.h"
#include "../generator/config/ruleconvert.h"
@@ -33,7 +34,9 @@
//common settings
std::string gPrefPath = "pref.ini", gDefaultExtConfig;
string_array gExcludeRemarks, gIncludeRemarks, gCustomRulesets, gStreamNodeRules, gTimeNodeRules;
string_array gExcludeRemarks, gIncludeRemarks;
RulesetConfigs gCustomRulesets;
RegexMatchConfigs gStreamNodeRules, gTimeNodeRules;
std::vector<ruleset_content> gRulesetContent;
std::string gListenAddress = "127.0.0.1", gDefaultUrls, gInsertUrls, gManagedConfigPrefix;
int gListenPort = 25500, gMaxPendingConns = 10, gMaxConcurThreads = 4;
@@ -46,9 +49,6 @@ extern int gLogLevel;
extern long gMaxAllowedDownloadSize;
string_map gAliases;
extern bool gServeFile;
extern std::string gServeFileRoot;
//global variables for template
std::string gTemplatePath = "templates";
string_map gTemplateVars;
@@ -61,7 +61,7 @@ std::string gGenerateProfiles;
std::mutex gMutexConfigure;
//preferences
string_array gRenames, gEmojis;
RegexMatchConfigs gRenames, gEmojis;
bool gAddEmoji = false, gRemoveEmoji = false, gAppendType = false, gFilterDeprecated = true;
tribool gUDP, gTFO, gSkipCertVerify, gTLS13, gEnableInsert;
bool gEnableSort = false, gUpdateStrict = false;
@@ -72,7 +72,7 @@ int gUpdateInterval = 0;
std::string gSortScript, gFilterScript;
std::string gClashBase;
string_array gCustomProxyGroups;
ProxyGroupConfigs gCustomProxyGroups;
std::string gSurgeBase, gSurfboardBase, gMellowBase, gQuanBase, gQuanXBase, gLoonBase, gSSSubBase;
std::string gSurgeSSRPath, gQuanXDevID;
@@ -86,11 +86,13 @@ bool gScriptCleanContext = false;
//cron system
bool gEnableCron = false;
string_array gCronTasks;
CronTaskConfigs gCronTasks;
extern WebServer webServer;
string_array gRegexBlacklist = {"(.*)*"};
void refreshRulesets(string_array &ruleset_list, std::vector<ruleset_content> &ruleset_content_array);
void refreshRulesets(RulesetConfigs &ruleset_list, std::vector<ruleset_content> &ruleset_content_array);
std::string parseProxy(const std::string &source)
{
@@ -222,7 +224,8 @@ std::string getRuleset(RESPONSE_CALLBACK_ARGS)
for(std::string &x : vArray)
x.insert(0, "ruleset,");
std::vector<ruleset_content> rca;
refreshRulesets(vArray, rca);
RulesetConfigs confs = INIBinding::from<RulesetConfig>::from_ini(vArray);
refreshRulesets(confs, rca);
for(ruleset_content &x : rca)
{
std::string content = x.rule_content.get();
@@ -256,8 +259,7 @@ std::string getRuleset(RESPONSE_CALLBACK_ARGS)
if(strLine[pose - 1] == '\r')
pose--;
}
else
pose -= posb;
pose -= posb;
return 0;
};
@@ -405,6 +407,50 @@ int importItems(string_array &target, bool scope_limit = true)
return 0;
}
toml::value parseToml(const std::string &content, const std::string &fname)
{
std::istringstream is(content);
return toml::parse(is, fname);
}
void importItems(std::vector<toml::value> &root, const std::string &import_key, bool scope_limit = true)
{
std::string content;
std::vector<toml::value> newRoot;
auto iter = root.begin();
size_t count = 0;
std::string proxy = parseProxy(gProxyConfig);
while(iter != root.end())
{
auto& table = iter->as_table();
if(table.find("import") == table.end())
newRoot.emplace_back(std::move(*iter));
else
{
const std::string &path = toml::get<std::string>(table.at("import"));
writeLog(0, "Trying to import items from " + path);
if(fileExist(path))
content = fileGet(path, scope_limit);
else if(isLink(path))
content = webGet(path, proxy, gCacheConfig);
else
writeLog(0, "File not found or not a valid URL: " + path, LOG_LEVEL_ERROR);
if(content.size())
{
auto items = parseToml(content, path);
auto list = toml::find<std::vector<toml::value>>(items, import_key);
count += list.size();
std::move(list.begin(), list.end(), std::back_inserter(newRoot));
}
}
iter++;
}
root.swap(newRoot);
writeLog(0, "Imported " + std::to_string(count) + " item(s).");
return;
}
void readRegexMatch(YAML::Node node, const std::string &delimiter, string_array &dest, bool scope_limit = true)
{
YAML::Node object;
@@ -488,8 +534,8 @@ void readGroup(YAML::Node node, string_array &dest, bool scope_limit = true)
std::string url = "http://www.gstatic.com/generate_204", interval = "300", tolerance, timeout;
object["name"] >>= name;
object["type"] >>= type;
tempArray.emplace_back("name=" + name);
tempArray.emplace_back("type=" + type);
tempArray.emplace_back(name);
tempArray.emplace_back(type);
object["url"] >>= url;
object["interval"] >>= interval;
object["tolerance"] >>= tolerance;
@@ -509,13 +555,13 @@ void readGroup(YAML::Node node, string_array &dest, bool scope_limit = true)
default:
if(tempArray.size() < 3)
continue;
tempArray.emplace_back("url=" + url);
tempArray.emplace_back("interval=" + interval + ",timeout=" + timeout + ",tolerance=" + tolerance);
tempArray.emplace_back(url);
tempArray.emplace_back(interval + "," + timeout + "," + tolerance);
}
strLine = std::accumulate(std::next(tempArray.begin()), tempArray.end(), tempArray[0], [](std::string a, std::string b) -> std::string
{
return std::move(a) + "," + std::move(b);
return std::move(a) + "`" + std::move(b);
});
dest.emplace_back(std::move(strLine));
}
@@ -555,7 +601,7 @@ void readRuleset(YAML::Node node, string_array &dest, bool scope_limit = true)
importItems(dest, scope_limit);
}
void refreshRulesets(string_array &ruleset_list, std::vector<ruleset_content> &ruleset_content_array)
void refreshRulesets(RulesetConfigs &ruleset_list, std::vector<ruleset_content> &ruleset_content_array)
{
eraseElements(ruleset_content_array);
std::string rule_group, rule_url, rule_url_typed, interval;
@@ -563,26 +609,18 @@ void refreshRulesets(string_array &ruleset_list, std::vector<ruleset_content> &r
std::string proxy = parseProxy(gProxyRuleset);
for(std::string &x : ruleset_list)
for(RulesetConfig &x : ruleset_list)
{
string_size pos = x.find(",");
if(pos == x.npos || pos == x.size() - 1)
continue;
rule_group = trim(x.substr(0, pos));
if(x.find("[]", pos + 1) == pos + 1)
rule_group = x.Group;
rule_url = x.Url;
std::string::size_type pos = x.Url.find("[]");
if(pos != std::string::npos)
{
rule_url = trim(x.substr(pos + 1));
writeLog(0, "Adding rule '" + rule_url.substr(2) + "," + rule_group + "'.", LOG_LEVEL_INFO);
rc = {rule_group, "", "", RULESET_SURGE, std::async(std::launch::async, [rule_url](){return rule_url;}), 0};
writeLog(0, "Adding rule '" + rule_url.substr(pos + 2) + "," + rule_group + "'.", LOG_LEVEL_INFO);
rc = {rule_group, "", "", RULESET_SURGE, std::async(std::launch::async, [=](){return rule_url.substr(pos);}), 0};
}
else
{
string_size pos2 = x.find(",", pos + 1);
rule_url = trim(x.substr(pos + 1, pos2 - pos - 1));
if(pos2 != x.npos)
interval = x.substr(pos2 + 1);
else
interval.clear();
ruleset_type type = RULESET_SURGE;
rule_url_typed = rule_url;
auto iter = std::find_if(RulesetTypes.begin(), RulesetTypes.end(), [rule_url](auto y){ return startsWith(rule_url, y.first); });
@@ -592,7 +630,7 @@ void refreshRulesets(string_array &ruleset_list, std::vector<ruleset_content> &r
type = iter->second;
}
writeLog(0, "Updating ruleset url '" + rule_url + "' with group '" + rule_group + "'.", LOG_LEVEL_INFO);
rc = {rule_group, rule_url, rule_url_typed, type, fetchFileAsync(rule_url, proxy, gCacheRuleset, gAsyncFetchRuleset), to_int(interval, 0)};
rc = {rule_group, rule_url, rule_url_typed, type, fetchFileAsync(rule_url, proxy, gCacheRuleset, gAsyncFetchRuleset), x.Interval};
}
ruleset_content_array.emplace_back(std::move(rc));
}
@@ -662,13 +700,15 @@ void readYAMLConf(YAML::Node &node)
if(section["stream_rule"].IsSequence())
{
readRegexMatch(section["stream_rule"], "|", tempArray, false);
safe_set_streams(tempArray);
auto configs = INIBinding::from<RegexMatchConfig>::from_ini(tempArray, "|");
safe_set_streams(configs);
eraseElements(tempArray);
}
if(section["time_rule"].IsSequence())
{
readRegexMatch(section["time_rule"], "|", tempArray, false);
safe_set_times(tempArray);
auto configs = INIBinding::from<RegexMatchConfig>::from_ini(tempArray, "|");
safe_set_times(configs);
eraseElements(tempArray);
}
}
@@ -696,7 +736,8 @@ void readYAMLConf(YAML::Node &node)
if(section["rename_node"].IsSequence())
{
readRegexMatch(section["rename_node"], "@", tempArray, false);
safe_set_renames(tempArray);
auto configs = INIBinding::from<RegexMatchConfig>::from_ini(tempArray, "@");
safe_set_renames(configs);
eraseElements(tempArray);
}
@@ -724,7 +765,8 @@ void readYAMLConf(YAML::Node &node)
if(section["rules"].IsSequence())
{
readEmoji(section["rules"], tempArray, false);
safe_set_emojis(tempArray);
auto configs = INIBinding::from<RegexMatchConfig>::from_ini(tempArray, ",");
safe_set_emojis(configs);
eraseElements(tempArray);
}
}
@@ -746,12 +788,20 @@ void readYAMLConf(YAML::Node &node)
}
const char *ruleset_title = section["rulesets"].IsDefined() ? "rulesets" : "surge_ruleset";
if(section[ruleset_title].IsSequence())
readRuleset(section[ruleset_title], gCustomRulesets, false);
{
string_array vArray;
readRuleset(section[ruleset_title], vArray, false);
gCustomRulesets = INIBinding::from<RulesetConfig>::from_ini(vArray);
}
}
const char *groups_title = node["proxy_groups"].IsDefined() ? "proxy_groups" : "proxy_group";
if(node[groups_title].IsDefined() && node[groups_title]["custom_proxy_group"].IsDefined())
readGroup(node[groups_title]["custom_proxy_group"], gCustomProxyGroups, false);
{
string_array vArray;
readGroup(node[groups_title]["custom_proxy_group"], vArray, false);
gCustomProxyGroups = INIBinding::from<ProxyGroupConfig>::from_ini(vArray);
}
if(node["template"].IsDefined())
{
@@ -771,26 +821,26 @@ void readYAMLConf(YAML::Node &node)
if(node["aliases"].IsSequence())
{
reset_redirect();
webServer.reset_redirect();
for(size_t i = 0; i < node["aliases"].size(); i++)
{
std::string uri, target;
node["aliases"][i]["uri"] >> uri;
node["aliases"][i]["target"] >> target;
append_redirect(uri, target);
webServer.append_redirect(uri, target);
}
}
if(node["tasks"].IsSequence())
{
gCronTasks.clear();
string_array vArray;
for(size_t i = 0; i < node["tasks"].size(); i++)
{
std::string name, exp, path, timeout;
node["tasks"][i]["import"] >> name;
if(name.size())
{
gCronTasks.emplace_back("!!import:" + name);
vArray.emplace_back("!!import:" + name);
continue;
}
node["tasks"][i]["name"] >> name;
@@ -798,10 +848,11 @@ void readYAMLConf(YAML::Node &node)
node["tasks"][i]["path"] >> path;
node["tasks"][i]["timeout"] >> timeout;
strLine = name + "`" + exp + "`" + path + "`" + timeout;
gCronTasks.push_back(std::move(strLine));
vArray.emplace_back(std::move(strLine));
}
importItems(gCronTasks, false);
gEnableCron = !gCronTasks.empty();
importItems(vArray, false);
gEnableCron = !vArray.empty();
gCronTasks = INIBinding::from<CronTaskConfig>::from_ini(vArray);
refresh_schedule();
}
@@ -809,8 +860,8 @@ void readYAMLConf(YAML::Node &node)
{
node["server"]["listen"] >> gListenAddress;
node["server"]["port"] >> gListenPort;
node["server"]["serve_file_root"] >>= gServeFileRoot;
gServeFile = !gServeFileRoot.empty();
node["server"]["serve_file_root"] >>= webServer.serve_file_root;
webServer.serve_file = !webServer.serve_file_root.empty();
}
if(node["advanced"].IsDefined())
@@ -866,6 +917,224 @@ void readYAMLConf(YAML::Node &node)
}
}
template <class T, class... U>
void find_if_exist(const toml::value &v, const toml::key &k, T& target, U&&... args)
{
if(v.contains(k)) target = toml::find<T>(v, k);
if constexpr (sizeof...(args)) find_if_exist(v, std::forward<U>(args)...);
}
std::string join(const string_array &arr, const std::string &delimiter = "|")
{
if(arr.size() == 0)
return "";
if(arr.size() == 1)
return arr[0];
return std::accumulate(arr.begin() + 1, arr.end(), arr[0], [&](const std::string &a, const std::string &b) { return a + delimiter + b; });
}
void operate_toml_kv_table(const std::vector<toml::table> &arr, const toml::key &key_name, const toml::key &value_name, std::function<void (const toml::value&, const toml::value&)> binary_op)
{
for(const toml::table &table : arr)
{
const auto &key = table.at(key_name), value = table.at(value_name);
binary_op(key, value);
}
}
void readTOMLConf(toml::value &root)
{
const auto &section_common = toml::find(root, "common");
string_array default_url, insert_url;
find_if_exist(section_common, "default_url", default_url, "insert_url", insert_url);
gDefaultUrls = join(default_url);
gInsertUrls = join(insert_url);
bool filter = false;
find_if_exist(section_common,
"api_mode", gAPIMode,
"api_access_token", gAccessToken,
"exclude_remarks", gExcludeRemarks,
"include_remarks", gIncludeRemarks,
"enable_insert", gEnableInsert,
"prepend_insert_url", gPrependInsert,
"enable_filter", filter,
"default_external_config", gDefaultExtConfig,
"base_path", gBasePath,
"clash_rule_base", gClashBase,
"surge_rule_base", gSurgeBase,
"surfboard_rule_base", gSurfboardBase,
"mellow_rule_base", gMellowBase,
"quan_rule_base", gQuanBase,
"quanx_rule_base", gQuanXBase,
"loon_rule_base", gLoonBase,
"proxy_config", gProxyConfig,
"proxy_ruleset", gProxyRuleset,
"proxy_subscription", gProxySubscription,
"append_proxy_type", gAppendType
);
if(filter)
find_if_exist(section_common, "filter_script", gFilterScript);
else
gFilterScript.clear();
safe_set_streams(toml::find_or<RegexMatchConfigs>(root, "userinfo", "stream_rule", RegexMatchConfigs{}));
safe_set_times(toml::find_or<RegexMatchConfigs>(root, "userinfo", "time_rule", RegexMatchConfigs{}));
const auto &section_node_pref = toml::find(root, "node_pref");
find_if_exist(section_node_pref,
"udp_flag", gUDP,
"tcp_fast_open_flag", gTFO,
"skip_cert_verify_flag", gSkipCertVerify,
"tls13_flag", gTLS13,
"sort_flag", gEnableSort,
"sort_script", gSortScript,
"filter_deprecated_nodes", gFilterDeprecated,
"append_sub_userinfo", gAppendUserinfo,
"clash_use_new_field_name", gClashUseNewField,
"clash_proxies_style", gClashProxiesStyle
);
auto renameconfs = toml::find_or<std::vector<toml::value>>(section_node_pref, "rename_node", {});
importItems(renameconfs, "rename_node", false);
safe_set_renames(toml::get<RegexMatchConfigs>(toml::value(renameconfs)));
const auto &section_managed = toml::find(root, "managed_config");
find_if_exist(section_managed,
"write_managed_config", gWriteManagedConfig,
"managed_config_prefix", gManagedConfigPrefix,
"config_update_interval", gUpdateInterval,
"config_update_strict", gUpdateStrict,
"quanx_device_id", gQuanXDevID
);
const auto &section_surge_external = toml::find(root, "surge_external_proxy");
find_if_exist(section_surge_external,
"surge_ssr_path", gSurgeSSRPath,
"resolve_hostname", gSurgeResolveHostname
);
const auto &section_emojis = toml::find(root, "emojis");
find_if_exist(section_emojis,
"add_emoji", gAddEmoji,
"remove_old_emoji", gRemoveEmoji
);
auto emojiconfs = toml::find_or<std::vector<toml::value>>(section_emojis, "emoji", {});
importItems(emojiconfs, "emoji", false);
safe_set_emojis(toml::get<RegexMatchConfigs>(toml::value(emojiconfs)));
auto groups = toml::find_or<std::vector<toml::value>>(root, "custom_groups", {});
importItems(groups, "custom_groups", false);
gCustomProxyGroups = toml::get<ProxyGroupConfigs>(toml::value(groups));
const auto &section_ruleset = toml::find(root, "ruleset");
find_if_exist(section_ruleset,
"enabled", gEnableRuleGen,
"overwrite_original_rules", gOverwriteOriginalRules,
"update_ruleset_on_request", gUpdateRulesetOnRequest
);
auto rulesets = toml::find_or<std::vector<toml::value>>(root, "rulesets", {});
importItems(rulesets, "rulesets", false);
gCustomRulesets = toml::get<RulesetConfigs>(toml::value(rulesets));
const auto &section_template = toml::find(root, "template");
gTemplatePath = toml::find_or(section_template, "template_path", "template");
eraseElements(gTemplateVars);
operate_toml_kv_table(toml::find_or<std::vector<toml::table>>(section_template, "globals", {}), "key", "value", [&](const toml::value &key, const toml::value &value)
{
gTemplateVars[key.as_string()] = value.as_string();
});
webServer.reset_redirect();
operate_toml_kv_table(toml::find_or<std::vector<toml::table>>(root, "aliases", {}), "uri", "target", [&](const toml::value &key, const toml::value &value)
{
webServer.append_redirect(key.as_string(), value.as_string());
});
auto tasks = toml::find_or<std::vector<toml::value>>(root, "tasks", {});
importItems(tasks, "tasks", false);
gCronTasks = toml::get<CronTaskConfigs>(toml::value(tasks));
const auto &section_server = toml::find(root, "server");
find_if_exist(section_server,
"listen", gListenAddress,
"port", gListenPort,
"serve_file_root", webServer.serve_file_root
);
webServer.serve_file = !webServer.serve_file_root.empty();
const auto &section_advanced = toml::find(root, "advanced");
std::string log_level;
bool enable_cache = true;
int cache_subscription = gCacheSubscription, cache_config = gCacheConfig, cache_ruleset = gCacheRuleset;
find_if_exist(section_advanced,
"log_level", log_level,
"print_debug_info", gPrintDbgInfo,
"max_pending_connections", gMaxPendingConns,
"max_concurrent_threads", gMaxConcurThreads,
"max_allowed_rulesets", gMaxAllowedRulesets,
"max_allowed_rules", gMaxAllowedRules,
"max_allowed_download_size", gMaxAllowedDownloadSize,
"enable_cache", enable_cache,
"cache_subscription", cache_subscription,
"cache_config", cache_config,
"cache_ruleset", cache_ruleset,
"script_clean_context", gScriptCleanContext,
"async_fetch_ruleset", gAsyncFetchRuleset,
"skip_failed_links", gSkipFailedLinks
);
if(gPrintDbgInfo)
gLogLevel = LOG_LEVEL_VERBOSE;
else
{
switch(hash_(log_level))
{
case "warn"_hash:
gLogLevel = LOG_LEVEL_WARNING;
break;
case "error"_hash:
gLogLevel = LOG_LEVEL_ERROR;
break;
case "fatal"_hash:
gLogLevel = LOG_LEVEL_FATAL;
break;
case "verbose"_hash:
gLogLevel = LOG_LEVEL_VERBOSE;
break;
case "debug"_hash:
gLogLevel = LOG_LEVEL_DEBUG;
break;
default:
gLogLevel = LOG_LEVEL_INFO;
}
}
if(enable_cache)
{
gCacheSubscription = cache_subscription;
gCacheConfig = cache_config;
gCacheRuleset = cache_ruleset;
}
else
{
gCacheSubscription = gCacheConfig = gCacheRuleset = 0;
}
}
void readConf()
{
guarded_mutex guard(gMutexConfigure);
@@ -886,10 +1155,18 @@ void readConf()
if(yaml.size() && yaml["common"])
return readYAMLConf(yaml);
}
toml::value conf = parseToml(prefdata, gPrefPath);
if(!conf.is_uninitialized() && toml::find_or<int>(conf, "version", 0))
return readTOMLConf(conf);
}
catch (YAML::Exception &e)
{
//ignore
//ignore yaml parse error
}
catch (toml::exception &e)
{
//ignore toml parse error
writeLog(0, e.what(), LOG_LEVEL_DEBUG);
}
INIReader ini;
@@ -960,7 +1237,8 @@ void readConf()
{
ini.GetAll("rename_node", tempArray);
importItems(tempArray, false);
safe_set_renames(tempArray);
auto configs = INIBinding::from<RegexMatchConfig>::from_ini(tempArray, "@");
safe_set_renames(configs);
eraseElements(tempArray);
}
}
@@ -972,14 +1250,16 @@ void readConf()
{
ini.GetAll("stream_rule", tempArray);
importItems(tempArray, false);
safe_set_streams(tempArray);
auto configs = INIBinding::from<RegexMatchConfig>::from_ini(tempArray, "|");
safe_set_streams(configs);
eraseElements(tempArray);
}
if(ini.ItemPrefixExist("time_rule"))
{
ini.GetAll("time_rule", tempArray);
importItems(tempArray, false);
safe_set_times(tempArray);
auto configs = INIBinding::from<RegexMatchConfig>::from_ini(tempArray, "|");
safe_set_times(configs);
eraseElements(tempArray);
}
}
@@ -998,7 +1278,8 @@ void readConf()
{
ini.GetAll("rule", tempArray);
importItems(tempArray, false);
safe_set_emojis(tempArray);
auto configs = INIBinding::from<RegexMatchConfig>::from_ini(tempArray, ",");
safe_set_emojis(configs);
eraseElements(tempArray);
}
@@ -1013,13 +1294,17 @@ void readConf()
ini.GetBoolIfExist("update_ruleset_on_request", gUpdateRulesetOnRequest);
if(ini.ItemPrefixExist("ruleset"))
{
ini.GetAll("ruleset", gCustomRulesets);
importItems(gCustomRulesets, true);
string_array vArray;
ini.GetAll("ruleset", vArray);
importItems(vArray, false);
gCustomRulesets = INIBinding::from<RulesetConfig>::from_ini(vArray);
}
else if(ini.ItemPrefixExist("surge_ruleset"))
{
ini.GetAll("surge_ruleset", gCustomRulesets);
importItems(gCustomRulesets, false);
string_array vArray;
ini.GetAll("surge_ruleset", vArray);
importItems(vArray, false);
gCustomRulesets = INIBinding::from<RulesetConfig>::from_ini(vArray);
}
}
else
@@ -1034,8 +1319,10 @@ void readConf()
ini.EnterSection("clash_proxy_group");
if(ini.ItemPrefixExist("custom_proxy_group"))
{
ini.GetAll("custom_proxy_group", gCustomProxyGroups);
importItems(gCustomProxyGroups, false);
string_array vArray;
ini.GetAll("custom_proxy_group", vArray);
importItems(vArray, false);
gCustomProxyGroups = INIBinding::from<ProxyGroupConfig>::from_ini(vArray);
}
ini.EnterSection("template");
@@ -1055,26 +1342,27 @@ void readConf()
{
ini.EnterSection("aliases");
ini.GetItems(tempmap);
reset_redirect();
webServer.reset_redirect();
for(auto &x : tempmap)
append_redirect(x.first, x.second);
webServer.append_redirect(x.first, x.second);
}
if(ini.SectionExist("tasks"))
{
gCronTasks.clear();
string_array vArray;
ini.EnterSection("tasks");
ini.GetAll("task", gCronTasks);
importItems(gCronTasks, false);
gEnableCron = !gCronTasks.empty();
ini.GetAll("task", vArray);
importItems(vArray, false);
gEnableCron = !vArray.empty();
gCronTasks = INIBinding::from<CronTaskConfig>::from_ini(vArray);
refresh_schedule();
}
ini.EnterSection("server");
ini.GetIfExist("listen", gListenAddress);
ini.GetIntIfExist("port", gListenPort);
gServeFileRoot = ini.Get("serve_file_root");
gServeFile = !gServeFileRoot.empty();
webServer.serve_file_root = ini.Get("serve_file_root");
webServer.serve_file = !webServer.serve_file_root.empty();
ini.EnterSection("advanced");
std::string log_level;
@@ -1135,8 +1423,8 @@ void readConf()
struct ExternalConfig
{
string_array custom_proxy_group;
string_array surge_ruleset;
ProxyGroupConfigs custom_proxy_group;
RulesetConfigs surge_ruleset;
std::string clash_rule_base;
std::string surge_rule_base;
std::string surfboard_rule_base;
@@ -1145,8 +1433,8 @@ struct ExternalConfig
std::string quanx_rule_base;
std::string loon_rule_base;
std::string sssub_rule_base;
string_array rename;
string_array emoji;
RegexMatchConfigs rename;
RegexMatchConfigs emoji;
string_array include;
string_array exclude;
template_args *tpl_args = NULL;
@@ -1176,28 +1464,41 @@ int loadExternalYAML(YAML::Node &node, ExternalConfig &ext)
const char *group_name = section["proxy_groups"].IsDefined() ? "proxy_groups" : "custom_proxy_group";
if(section[group_name].size())
readGroup(section[group_name], ext.custom_proxy_group, gAPIMode);
{
string_array vArray;
readGroup(section[group_name], vArray, gAPIMode);
ext.custom_proxy_group = INIBinding::from<ProxyGroupConfig>::from_ini(vArray);
}
const char *ruleset_name = section["rulesets"].IsDefined() ? "rulesets" : "surge_ruleset";
if(section[ruleset_name].size())
{
readRuleset(section[ruleset_name], ext.surge_ruleset, gAPIMode);
if(gMaxAllowedRulesets && ext.surge_ruleset.size() > gMaxAllowedRulesets)
string_array vArray;
readRuleset(section[ruleset_name], vArray, gAPIMode);
if(gMaxAllowedRulesets && vArray.size() > gMaxAllowedRulesets)
{
writeLog(0, "Ruleset count in external config has exceeded limit.", LOG_LEVEL_WARNING);
eraseElements(ext.surge_ruleset);
return -1;
}
ext.surge_ruleset = INIBinding::from<RulesetConfig>::from_ini(vArray);
}
if(section["rename_node"].size())
readRegexMatch(section["rename_node"], "@", ext.rename, gAPIMode);
{
string_array vArray;
readRegexMatch(section["rename_node"], "@", vArray, gAPIMode);
ext.rename = INIBinding::from<RegexMatchConfig>::from_ini(vArray, "@");
}
ext.add_emoji = safe_as<std::string>(section["add_emoji"]);
ext.remove_old_emoji = safe_as<std::string>(section["remove_old_emoji"]);
const char *emoji_name = section["emojis"].IsDefined() ? "emojis" : "emoji";
if(section[emoji_name].size())
readEmoji(section[emoji_name], ext.emoji, gAPIMode);
{
string_array vArray;
readEmoji(section[emoji_name], vArray, gAPIMode);
ext.emoji = INIBinding::from<RegexMatchConfig>::from_ini(vArray, ",");
}
section["include_remarks"] >> ext.include;
section["exclude_remarks"] >> ext.exclude;
@@ -1216,6 +1517,57 @@ int loadExternalYAML(YAML::Node &node, ExternalConfig &ext)
return 0;
}
int loadExternalTOML(toml::value &root, ExternalConfig &ext)
{
const auto &section = toml::find(root, "custom");
find_if_exist(section,
"enable_rule_generator", ext.enable_rule_generator,
"overwrite_original_rules", ext.overwrite_original_rules,
"clash_rule_base", ext.clash_rule_base,
"surge_rule_base", ext.surge_ruleset,
"surfboard_rule_base", ext.surfboard_rule_base,
"mellow_rule_base", ext.mellow_rule_base,
"quan_rule_base", ext.quan_rule_base,
"quanx_rule_base", ext.quanx_rule_base,
"sssub_rule_base", ext.sssub_rule_base,
"add_emoji", ext.add_emoji,
"remove_old_emoji", ext.remove_old_emoji,
"include_remarks", ext.include,
"exclude_remarks", ext.exclude
);
if(ext.tpl_args != nullptr) operate_toml_kv_table(toml::find_or<std::vector<toml::table>>(section, "template_args", {}), "key", "value",
[&](const toml::value &key, const toml::value &value)
{
std::string val = toml::format(value);
ext.tpl_args->local_vars[key.as_string()] = val;
});
auto groups = toml::find_or<std::vector<toml::value>>(root, "custom_groups", {});
importItems(groups, "custom_groups", false);
ext.custom_proxy_group = toml::get<ProxyGroupConfigs>(toml::value(groups));
auto rulesets = toml::find_or<std::vector<toml::value>>(root, "rulesets", {});
importItems(rulesets, "rulesets", false);
if(gMaxAllowedRulesets && rulesets.size() > gMaxAllowedRulesets)
{
writeLog(0, "Ruleset count in external config has exceeded limit. ", LOG_LEVEL_WARNING);
return -1;
}
ext.surge_ruleset = toml::get<RulesetConfigs>(toml::value(rulesets));
auto emojiconfs = toml::find_or<std::vector<toml::value>>(root, "emoji", {});
importItems(emojiconfs, "emoji", false);
ext.emoji = toml::get<RegexMatchConfigs>(toml::value(emojiconfs));
auto renameconfs = toml::find_or<std::vector<toml::value>>(root, "rename_node", {});
importItems(renameconfs, "rename_node", false);
ext.rename = toml::get<RegexMatchConfigs>(toml::value(renameconfs));
return 0;
}
int loadExternalConfig(std::string &path, ExternalConfig &ext)
{
std::string base_content, proxy = parseProxy(gProxyConfig), config = fetchFile(path, proxy, gCacheConfig);
@@ -1227,11 +1579,18 @@ int loadExternalConfig(std::string &path, ExternalConfig &ext)
YAML::Node yaml = YAML::Load(base_content);
if(yaml.size() && yaml["custom"].IsDefined())
return loadExternalYAML(yaml, ext);
toml::value conf = parseToml(base_content, path);
if(!conf.is_uninitialized() && toml::find_or<int>(conf, "version", 0))
return loadExternalTOML(conf, ext);
}
catch (YAML::Exception &e)
{
//ignore
}
catch (toml::exception &e)
{
//ignore
}
INIReader ini;
ini.store_isolated_line = true;
@@ -1246,20 +1605,23 @@ int loadExternalConfig(std::string &path, ExternalConfig &ext)
ini.EnterSection("custom");
if(ini.ItemPrefixExist("custom_proxy_group"))
{
ini.GetAll("custom_proxy_group", ext.custom_proxy_group);
importItems(ext.custom_proxy_group, gAPIMode);
string_array vArray;
ini.GetAll("custom_proxy_group", vArray);
importItems(vArray, gAPIMode);
ext.custom_proxy_group = INIBinding::from<ProxyGroupConfig>::from_ini(vArray);
}
std::string ruleset_name = ini.ItemPrefixExist("ruleset") ? "ruleset" : "surge_ruleset";
if(ini.ItemPrefixExist(ruleset_name))
{
ini.GetAll(ruleset_name, ext.surge_ruleset);
importItems(ext.surge_ruleset, gAPIMode);
if(gMaxAllowedRulesets && ext.surge_ruleset.size() > gMaxAllowedRulesets)
string_array vArray;
ini.GetAll(ruleset_name, vArray);
importItems(vArray, gAPIMode);
if(gMaxAllowedRulesets && vArray.size() > gMaxAllowedRulesets)
{
writeLog(0, "Ruleset count in external config has exceeded limit. ", LOG_LEVEL_WARNING);
eraseElements(ext.surge_ruleset);
return -1;
}
ext.surge_ruleset = INIBinding::from<RulesetConfig>::from_ini(vArray);
}
ini.GetIfExist("clash_rule_base", ext.clash_rule_base);
@@ -1276,22 +1638,26 @@ int loadExternalConfig(std::string &path, ExternalConfig &ext)
if(ini.ItemPrefixExist("rename"))
{
ini.GetAll("rename", ext.rename);
importItems(ext.rename, gAPIMode);
string_array vArray;
ini.GetAll("rename", vArray);
importItems(vArray, gAPIMode);
ext.rename = INIBinding::from<RegexMatchConfig>::from_ini(vArray, "@");
}
ext.add_emoji = ini.Get("add_emoji");
ext.remove_old_emoji = ini.Get("remove_old_emoji");
if(ini.ItemPrefixExist("emoji"))
{
ini.GetAll("emoji", ext.emoji);
importItems(ext.emoji, gAPIMode);
string_array vArray;
ini.GetAll("emoji", vArray);
importItems(vArray, gAPIMode);
ext.emoji = INIBinding::from<RegexMatchConfig>::from_ini(vArray, ",");
}
if(ini.ItemPrefixExist("include_remarks"))
ini.GetAll("include_remarks", ext.include);
if(ini.ItemPrefixExist("exclude_remarks"))
ini.GetAll("exclude_remarks", ext.exclude);
if(ini.SectionExist("template") && ext.tpl_args != NULL)
if(ini.SectionExist("template") && ext.tpl_args != nullptr)
{
ini.EnterSection("template");
string_multimap tempmap;
@@ -1354,7 +1720,9 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
tribool argPrependInsert = getUrlArg(argument, "prepend"), argGenClassicalRuleProvider = getUrlArg(argument, "classic"), argTLS13 = getUrlArg(argument, "tls13");
std::string base_content, output_content;
string_array lCustomProxyGroups = gCustomProxyGroups, lCustomRulesets = gCustomRulesets, lIncludeRemarks = gIncludeRemarks, lExcludeRemarks = gExcludeRemarks;
ProxyGroupConfigs lCustomProxyGroups = gCustomProxyGroups;
RulesetConfigs lCustomRulesets = gCustomRulesets;
string_array lIncludeRemarks = gIncludeRemarks, lExcludeRemarks = gExcludeRemarks;
std::vector<ruleset_content> lRulesetContent;
extra_settings ext;
std::string subInfo, dummy;
@@ -1488,11 +1856,17 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
{
/// loading custom groups
if(argCustomGroups.size() && !ext.nodelist)
lCustomProxyGroups = split(argCustomGroups, "@");
{
string_array vArray = split(argCustomGroups, "@");
lCustomProxyGroups = INIBinding::from<ProxyGroupConfig>::from_ini(vArray);
}
/// loading custom rulesets
if(argCustomRulesets.size() && !ext.nodelist)
lCustomRulesets = split(argCustomRulesets, "@");
{
string_array vArray = split(argCustomRulesets, "@");
lCustomRulesets = INIBinding::from<RulesetConfig>::from_ini(vArray);
}
}
}
if(ext.enable_rule_generator && !ext.nodelist && !lSimpleSubscription)
@@ -1517,7 +1891,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
if(ext.add_emoji && ext.emoji_array.empty())
ext.emoji_array = safe_get_emojis();
if(argRenames.size())
ext.rename_array = split(argRenames, "`");
ext.rename_array = INIBinding::from<RegexMatchConfig>::from_ini(split(argRenames, "`"), "@");
else if(ext.rename_array.empty())
ext.rename_array = safe_get_renames();
@@ -1537,7 +1911,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
}
//start parsing urls
string_array stream_temp = safe_get_streams(), time_temp = safe_get_times();
RegexMatchConfigs stream_temp = safe_get_streams(), time_temp = safe_get_times();
//loading urls
string_array urls;
@@ -1645,7 +2019,8 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
}
}
*/
script_safe_runner(ext.js_runtime, ext.js_context, [&](qjs::Context &ctx){
script_safe_runner(ext.js_runtime, ext.js_context, [&](qjs::Context &ctx)
{
try
{
ctx.eval(filterScript);
@@ -1684,7 +2059,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
}
*/
string_array dummy_group;
ProxyGroupConfigs dummy_group;
std::vector<ruleset_content> dummy_ruleset;
std::string managed_url = base64Decode(urlDecode(getUrlArg(argument, "profile_data")));
if(managed_url.empty())
@@ -1999,10 +2374,12 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
proxy = parseProxy(gProxySubscription);
eraseElements(dummy_str_array);
RegexMatchConfigs dummy_regex_array;
std::string subInfo;
parse_settings parse_set;
parse_set.proxy = &proxy;
parse_set.exclude_remarks = parse_set.include_remarks = parse_set.stream_rules = parse_set.time_rules = &dummy_str_array;
parse_set.exclude_remarks = parse_set.include_remarks = &dummy_str_array;
parse_set.stream_rules = parse_set.time_rules = &dummy_regex_array;
parse_set.request_header = &request.headers;
parse_set.sub_info = &subInfo;
parse_set.authorized = !gAPIMode;
@@ -2039,7 +2416,8 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
ext.tls13 = gTLS13;
ext.clash_proxies_style = gClashProxiesStyle;
proxyToClash(nodes, clash, dummy_str_array, false, ext);
ProxyGroupConfigs dummy_groups;
proxyToClash(nodes, clash, dummy_groups, false, ext);
section.clear();
ini.GetItems("Proxy", section);
+2 -1
View File
@@ -5,10 +5,11 @@
#include <map>
#include <inja.hpp>
#include "../config/ruleset.h"
#include "../generator/config/subexport.h"
#include "../server/webserver.h"
void refreshRulesets(string_array &ruleset_list, std::vector<ruleset_content> &rca);
void refreshRulesets(RulesetConfigs &ruleset_list, std::vector<ruleset_content> &rca);
void readConf();
int simpleGenerator();
std::string convertRuleset(const std::string &content, int type);
+10 -10
View File
@@ -9,52 +9,52 @@
//safety lock for multi-thread
std::mutex on_emoji, on_rename, on_stream, on_time;
extern string_array gEmojis, gRenames;
extern string_array gStreamNodeRules, gTimeNodeRules;
extern RegexMatchConfigs gEmojis, gRenames;
extern RegexMatchConfigs gStreamNodeRules, gTimeNodeRules;
string_array safe_get_emojis()
RegexMatchConfigs safe_get_emojis()
{
guarded_mutex guard(on_emoji);
return gEmojis;
}
string_array safe_get_renames()
RegexMatchConfigs safe_get_renames()
{
guarded_mutex guard(on_rename);
return gRenames;
}
string_array safe_get_streams()
RegexMatchConfigs safe_get_streams()
{
guarded_mutex guard(on_stream);
return gStreamNodeRules;
}
string_array safe_get_times()
RegexMatchConfigs safe_get_times()
{
guarded_mutex guard(on_time);
return gTimeNodeRules;
}
void safe_set_emojis(string_array &data)
void safe_set_emojis(RegexMatchConfigs data)
{
guarded_mutex guard(on_emoji);
gEmojis.swap(data);
}
void safe_set_renames(string_array &data)
void safe_set_renames(RegexMatchConfigs data)
{
guarded_mutex guard(on_rename);
gRenames.swap(data);
}
void safe_set_streams(string_array &data)
void safe_set_streams(RegexMatchConfigs data)
{
guarded_mutex guard(on_stream);
gStreamNodeRules.swap(data);
}
void safe_set_times(string_array &data)
void safe_set_times(RegexMatchConfigs data)
{
guarded_mutex guard(on_time);
gTimeNodeRules.swap(data);
+9 -8
View File
@@ -6,21 +6,22 @@
#include <yaml-cpp/yaml.h>
#include "../config/regmatch.h"
#include "../utils/ini_reader/ini_reader.h"
#include "../utils/string.h"
using guarded_mutex = std::lock_guard<std::mutex>;
string_array safe_get_emojis();
string_array safe_get_renames();
string_array safe_get_streams();
string_array safe_get_times();
RegexMatchConfigs safe_get_emojis();
RegexMatchConfigs safe_get_renames();
RegexMatchConfigs safe_get_streams();
RegexMatchConfigs safe_get_times();
YAML::Node safe_get_clash_base();
INIReader safe_get_mellow_base();
void safe_set_emojis(string_array &data);
void safe_set_renames(string_array &data);
void safe_set_streams(string_array &data);
void safe_set_times(string_array &data);
void safe_set_emojis(RegexMatchConfigs data);
void safe_set_renames(RegexMatchConfigs data);
void safe_set_streams(RegexMatchConfigs data);
void safe_set_times(RegexMatchConfigs data);
std::shared_future<std::string> fetchFileAsync(const std::string &path, const std::string &proxy, int cache_ttl, bool async = false);
std::string fetchFile(const std::string &path, const std::string &proxy, int cache_ttl);
+1 -1
View File
@@ -98,7 +98,7 @@ RWLock cache_rw_lock;
long gMaxAllowedDownloadSize = 1048576L;
//std::string user_agent_str = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36";
std::string user_agent_str = "subconverter/" VERSION " cURL/" LIBCURL_VERSION;
static std::string user_agent_str = "subconverter/" VERSION " cURL/" LIBCURL_VERSION;
struct curl_progress_data
{
+33 -23
View File
@@ -6,6 +6,7 @@
#include <sys/types.h>
#include <dirent.h>
#include "config/ruleset.h"
#include "handler/interfaces.h"
#include "handler/webget.h"
#include "script/cron.h"
@@ -25,10 +26,12 @@
extern std::string gPrefPath, gAccessToken, gListenAddress, gGenerateProfiles, gManagedConfigPrefix;
extern bool gAPIMode, gGeneratorMode, gCFWChildProcess, gUpdateRulesetOnRequest;
extern int gListenPort, gMaxConcurThreads, gMaxPendingConns;
extern string_array gCustomRulesets;
extern RulesetConfigs gCustomRulesets;
extern std::vector<ruleset_content> gRulesetContent;
extern bool gEnableCron;
WebServer webServer;
#ifndef _WIN32
void SetConsoleTitle(const std::string &title)
{
@@ -105,7 +108,7 @@ void signal_handler(int sig)
#endif // _WIN32
case SIGTERM:
case SIGINT:
stop_web_server();
webServer.stop_web_server();
break;
}
}
@@ -122,10 +125,17 @@ int main(int argc, char *argv[])
std::string prgpath = argv[0];
setcd(prgpath); //first switch to program directory
#endif // _DEBUG
if(fileExist("pref.yml"))
if(fileExist("pref.toml"))
gPrefPath = "pref.toml";
else if(fileExist("pref.yml"))
gPrefPath = "pref.yml";
else if(!fileExist("pref.ini"))
{
if(fileExist("pref.example.toml"))
{
fileCopy("pref.example.toml", "pref.toml");
gPrefPath = "pref.toml";
}
if(fileExist("pref.example.yml"))
{
fileCopy("pref.example.yml", "pref.yml");
@@ -173,18 +183,18 @@ int main(int argc, char *argv[])
return simpleGenerator();
/*
append_response("GET", "/", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
webServer.append_response("GET", "/", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
return "subconverter " VERSION " backend\n";
});
*/
append_response("GET", "/version", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
webServer.append_response("GET", "/version", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
return "subconverter " VERSION " backend\n";
});
append_response("GET", "/refreshrules", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
webServer.append_response("GET", "/refreshrules", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
if(gAccessToken.size())
{
@@ -199,7 +209,7 @@ int main(int argc, char *argv[])
return "done\n";
});
append_response("GET", "/readconf", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
webServer.append_response("GET", "/readconf", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
if(gAccessToken.size())
{
@@ -216,7 +226,7 @@ int main(int argc, char *argv[])
return "done\n";
});
append_response("POST", "/updateconf", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
webServer.append_response("POST", "/updateconf", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
if(gAccessToken.size())
{
@@ -244,7 +254,7 @@ int main(int argc, char *argv[])
return "done\n";
});
append_response("GET", "/flushcache", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
webServer.append_response("GET", "/flushcache", "text/plain", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
if(getUrlArg(request.argument, "token") != gAccessToken)
{
@@ -255,41 +265,41 @@ int main(int argc, char *argv[])
return "done";
});
append_response("GET", "/sub", "text/plain;charset=utf-8", subconverter);
webServer.append_response("GET", "/sub", "text/plain;charset=utf-8", subconverter);
append_response("GET", "/sub2clashr", "text/plain;charset=utf-8", simpleToClashR);
webServer.append_response("GET", "/sub2clashr", "text/plain;charset=utf-8", simpleToClashR);
append_response("GET", "/surge2clash", "text/plain;charset=utf-8", surgeConfToClash);
webServer.append_response("GET", "/surge2clash", "text/plain;charset=utf-8", surgeConfToClash);
append_response("GET", "/getruleset", "text/plain;charset=utf-8", getRuleset);
webServer.append_response("GET", "/getruleset", "text/plain;charset=utf-8", getRuleset);
append_response("GET", "/getprofile", "text/plain;charset=utf-8", getProfile);
webServer.append_response("GET", "/getprofile", "text/plain;charset=utf-8", getProfile);
append_response("GET", "/qx-script", "text/plain;charset=utf-8", getScript);
webServer.append_response("GET", "/qx-script", "text/plain;charset=utf-8", getScript);
append_response("GET", "/qx-rewrite", "text/plain;charset=utf-8", getRewriteRemote);
webServer.append_response("GET", "/qx-rewrite", "text/plain;charset=utf-8", getRewriteRemote);
append_response("GET", "/render", "text/plain;charset=utf-8", renderTemplate);
webServer.append_response("GET", "/render", "text/plain;charset=utf-8", renderTemplate);
append_response("GET", "/convert", "text/plain;charset=utf-8", getConvertedRuleset);
webServer.append_response("GET", "/convert", "text/plain;charset=utf-8", getConvertedRuleset);
if(!gAPIMode)
{
append_response("GET", "/get", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
webServer.append_response("GET", "/get", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
std::string url = urlDecode(getUrlArg(request.argument, "url"));
return webGet(url, "");
});
append_response("GET", "/getlocal", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
webServer.append_response("GET", "/getlocal", "text/plain;charset=utf-8", [](RESPONSE_CALLBACK_ARGS) -> std::string
{
return fileGet(urlDecode(getUrlArg(request.argument, "path")));
});
}
//append_response("POST", "/create-profile", "text/plain;charset=utf-8", createProfile);
//webServer.append_response("POST", "/create-profile", "text/plain;charset=utf-8", createProfile);
//append_response("GET", "/list-profiles", "text/plain;charset=utf-8", listProfiles);
//webServer.append_response("GET", "/list-profiles", "text/plain;charset=utf-8", listProfiles);
std::string env_port = getEnv("PORT");
if(env_port.size())
@@ -297,7 +307,7 @@ int main(int argc, char *argv[])
listener_args args = {gListenAddress, gListenPort, gMaxPendingConns, gMaxConcurThreads, cron_tick_caller, 200};
//std::cout<<"Serving HTTP @ http://"<<listen_address<<":"<<listen_port<<std::endl;
writeLog(0, "Startup completed. Serving HTTP @ http://" + gListenAddress + ":" + std::to_string(gListenPort), LOG_LEVEL_INFO);
start_web_server_multi(&args);
webServer.start_web_server_multi(&args);
#ifdef _WIN32
WSACleanup();
+9 -18
View File
@@ -3,6 +3,7 @@
#include <cmath>
#include <time.h>
#include "../config/regmatch.h"
#include "../parser/config/proxy.h"
#include "../utils/base64/base64.h"
#include "../utils/rapidjson_extra.h"
@@ -82,9 +83,9 @@ bool getSubInfoFromHeader(const std::string &header, std::string &result)
return false;
}
bool getSubInfoFromNodes(const std::vector<Proxy> &nodes, const string_array &stream_rules, const string_array &time_rules, std::string &result)
bool getSubInfoFromNodes(const std::vector<Proxy> &nodes, const RegexMatchConfigs &stream_rules, const RegexMatchConfigs &time_rules, std::string &result)
{
std::string remarks, pattern, target, stream_info, time_info, retStr;
std::string remarks, stream_info, time_info, retStr;
string_size spos;
for(const Proxy &x : nodes)
@@ -92,16 +93,11 @@ bool getSubInfoFromNodes(const std::vector<Proxy> &nodes, const string_array &st
remarks = x.Remark;
if(!stream_info.size())
{
for(const std::string &y : stream_rules)
for(const RegexMatchConfig &y : stream_rules)
{
spos = y.rfind("|");
if(spos == y.npos)
continue;
pattern = y.substr(0, spos);
target = y.substr(spos + 1);
if(regMatch(remarks, pattern))
if(regMatch(remarks, y.Match))
{
retStr = regReplace(remarks, pattern, target);
retStr = regReplace(remarks, y.Match, y.Replace);
if(retStr != remarks)
{
stream_info = retStr;
@@ -116,16 +112,11 @@ bool getSubInfoFromNodes(const std::vector<Proxy> &nodes, const string_array &st
remarks = x.Remark;
if(!time_info.size())
{
for(const std::string &y : time_rules)
for(const RegexMatchConfig &y : time_rules)
{
spos = y.rfind("|");
if(spos == y.npos)
continue;
pattern = y.substr(0, spos);
target = y.substr(spos + 1);
if(regMatch(remarks, pattern))
if(regMatch(remarks, y.Match))
{
retStr = regReplace(remarks, pattern, target);
retStr = regReplace(remarks, y.Match, y.Replace);
if(retStr != remarks)
{
time_info = retStr;
+2 -1
View File
@@ -5,9 +5,10 @@
#include "../utils/string.h"
#include "config/proxy.h"
#include "../config/regmatch.h"
bool getSubInfoFromHeader(const std::string &header, std::string &result);
bool getSubInfoFromNodes(const std::vector<Proxy> &nodes, const string_array &stream_rules, const string_array &time_rules, std::string &result);
bool getSubInfoFromNodes(const std::vector<Proxy> &nodes, const RegexMatchConfigs &stream_rules, const RegexMatchConfigs &time_rules, std::string &result);
bool getSubInfoFromSSD(const std::string &sub, std::string &result);
unsigned long long streamToInt(const std::string &stream);
+32 -9
View File
@@ -96,12 +96,14 @@ void httpConstruct(Proxy &node, const std::string &group, const std::string &rem
node.TLSSecure = tls;
}
void trojanConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &host, bool tlssecure, tribool udp, tribool tfo, tribool scv, tribool tls13)
void trojanConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &network, const std::string &host, const std::string &path, bool tlssecure, tribool udp, tribool tfo, tribool scv, tribool tls13)
{
commonConstruct(node, ProxyType::Trojan, group, remarks, server, port, udp, tfo, scv, tls13);
node.Password = password;
node.Host = host;
node.TLSSecure = tlssecure;
node.TransferProtocol = network.empty() ? "tcp" : network;
node.Path = path;
}
void snellConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &obfs, const std::string &host, tribool udp, tribool tfo, tribool scv)
@@ -761,7 +763,7 @@ void explodeTrojan(std::string trojan, Proxy &node)
if(group.empty())
group = TROJAN_DEFAULT_GROUP;
trojanConstruct(node, group, remark, server, port, psk, host, true, tribool(), tfo, scv);
trojanConstruct(node, group, remark, server, port, psk, "", host, "", true, tribool(), tfo, scv);
}
void explodeQuan(const std::string &quan, Proxy &node)
@@ -911,10 +913,12 @@ void explodeNetch(std::string netch, Proxy &node)
break;
case "Trojan"_hash:
host = GetMember(json, "Host");
path = GetMember(json, "Path");
transprot = GetMember(json, "TransferProtocol");
tls = GetMember(json, "TLSSecure");
if(group.empty())
group = TROJAN_DEFAULT_GROUP;
trojanConstruct(node, group, remark, address, port, password, host, tls == "true", udp, tfo, scv);
trojanConstruct(node, group, remark, address, port, password, transprot, host, path, tls == "true", udp, tfo, scv);
break;
case "Snell"_hash:
obfs = GetMember(json, "OBFS");
@@ -968,9 +972,18 @@ void explodeClash(Node yamlnode, std::vector<Proxy> &nodes)
edge.clear();
break;
case "ws"_hash:
path = singleproxy["ws-path"].IsDefined() ? safe_as<std::string>(singleproxy["ws-path"]) : "/";
singleproxy["ws-headers"]["Host"] >>= host;
singleproxy["ws-headers"]["Edge"] >>= edge;
if(singleproxy["ws-opts"].IsDefined())
{
path = singleproxy["ws-opts"]["path"].IsDefined() ? safe_as<std::string>(singleproxy["ws-opts"]["path"]) : "/";
singleproxy["ws-opts"]["headers"]["Host"] >>= host;
singleproxy["ws-opts"]["headers"]["Edge"] >>= edge;
}
else
{
path = singleproxy["ws-path"].IsDefined() ? safe_as<std::string>(singleproxy["ws-path"]) : "/";
singleproxy["ws-headers"]["Host"] >>= host;
singleproxy["ws-headers"]["Edge"] >>= edge;
}
break;
case "h2"_hash:
singleproxy["h2-opts"]["path"] >>= path;
@@ -1097,8 +1110,18 @@ void explodeClash(Node yamlnode, std::vector<Proxy> &nodes)
group = TROJAN_DEFAULT_GROUP;
singleproxy["password"] >>= password;
singleproxy["sni"] >>= host;
if(safe_as<std::string>(singleproxy["network"]) == "grpc")
{
net = "grpc";
singleproxy["grpc-opts"]["grpc-service-name"] >>= path;
}
else
{
net = "tcp";
path.clear();
}
trojanConstruct(node, group, ps, server, port, password, host, true, udp, tfo, scv);
trojanConstruct(node, group, ps, server, port, password, net, host, path, true, udp, tfo, scv);
break;
case "snell"_hash:
group = SNELL_DEFAULT_GROUP;
@@ -1574,7 +1597,7 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes)
if(host.empty() && !isIPv4(server) && !isIPv6(server))
host = server;
trojanConstruct(node, TROJAN_DEFAULT_GROUP, remarks, server, port, password, host, true, udp, tfo, scv);
trojanConstruct(node, TROJAN_DEFAULT_GROUP, remarks, server, port, password, "", host, "", true, udp, tfo, scv);
break;
case "snell"_hash:
server = trim(configs[1]);
@@ -1840,7 +1863,7 @@ bool explodeSurge(std::string surge, std::vector<Proxy> &nodes)
if(host.empty() && !isIPv4(server) && !isIPv6(server))
host = server;
trojanConstruct(node, TROJAN_DEFAULT_GROUP, remarks, server, port, password, host, tls == "true", udp, tfo, scv, tls13);
trojanConstruct(node, TROJAN_DEFAULT_GROUP, remarks, server, port, password, "", host, "", tls == "true", udp, tfo, scv, tls13);
break;
case "http"_hash: //quantumult x style http links
server = trim(configs[0].substr(0, configs[0].rfind(":")));
+1 -1
View File
@@ -25,7 +25,7 @@ void ssrConstruct(Proxy &node, const std::string &group, const std::string &rema
void ssConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &method, const std::string &plugin, const std::string &pluginopts, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool());
void socksConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &username, const std::string &password, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
void httpConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &username, const std::string &password, bool tls, tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool());
void trojanConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &host, bool tlssecure, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool());
void trojanConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &network, const std::string &host, const std::string &path, bool tlssecure, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool(), tribool tls13 = tribool());
void snellConstruct(Proxy &node, const std::string &group, const std::string &remarks, const std::string &server, const std::string &port, const std::string &password, const std::string &obfs, const std::string &host, tribool udp = tribool(), tribool tfo = tribool(), tribool scv = tribool());
void explodeVmess(std::string vmess, Proxy &node);
void explodeSSR(std::string ssr, Proxy &node);
+13 -20
View File
@@ -2,6 +2,7 @@
#include <iostream>
#include <libcron/Cron.h>
#include "../config/crontask.h"
#include "../handler/interfaces.h"
#include "../handler/multithread.h"
#include "../server/webserver.h"
@@ -11,7 +12,7 @@
#include "script_quickjs.h"
extern bool gEnableCron;
extern string_array gCronTasks;
extern CronTaskConfigs gCronTasks;
extern std::string gProxyConfig, gAccessToken;
extern int gCacheConfig;
@@ -48,13 +49,9 @@ int timeout_checker(JSRuntime *rt, void *opaque)
void refresh_schedule()
{
cron.clear_schedules();
for(std::string &x : gCronTasks)
for(const CronTaskConfig &x : gCronTasks)
{
string_array arguments = split(x, "`");
if(arguments.size() < 3)
continue;
std::string &name = arguments[0], &cronexp = arguments[1], &path = arguments[2];
cron.add_schedule(name, cronexp, [=](auto &)
cron.add_schedule(x.Name, x.CronExp, [=](auto &)
{
qjs::Runtime runtime;
qjs::Context context(runtime);
@@ -64,18 +61,18 @@ void refresh_schedule()
script_context_init(context);
defer(script_cleanup(context);)
std::string proxy = parseProxy(gProxyConfig);
std::string script = fetchFile(path, proxy, gCacheConfig);
std::string script = fetchFile(x.Path, proxy, gCacheConfig);
if(script.empty())
{
writeLog(0, "Script '" + name + "' run failed: file is empty or not exist!", LOG_LEVEL_WARNING);
writeLog(0, "Script '" + x.Name + "' run failed: file is empty or not exist!", LOG_LEVEL_WARNING);
return;
}
script_info info;
if(arguments.size() >= 4 && !arguments[3].empty())
if(x.Timeout > 0)
{
info.begin_time = time(NULL);
info.timeout = to_int(arguments[3], 0);
info.name = name;
info.timeout = x.Timeout;
info.name = x.Name;
JS_SetInterruptHandler(JS_GetRuntime(context.ctx), timeout_checker, &info);
}
context.eval(script);
@@ -109,19 +106,15 @@ std::string list_cron_schedule(RESPONSE_CALLBACK_ARGS)
writer.Int(200);
writer.Key("tasks");
writer.StartArray();
for(std::string &x : gCronTasks)
for(const CronTaskConfig &x : gCronTasks)
{
string_array arguments = split(x, "`");
if(arguments.size() < 3)
continue;
writer.StartObject();
std::string &name = arguments[0], &cronexp = arguments[1], &path = arguments[2];
writer.Key("name");
writer.String(name.data());
writer.String(x.Name.data());
writer.Key("cronexp");
writer.String(cronexp.data());
writer.String(x.CronExp.data());
writer.Key("path");
writer.String(path.data());
writer.String(x.Path.data());
writer.EndObject();
}
writer.EndArray();
+40 -6
View File
@@ -3,8 +3,13 @@
#include <string>
#include <map>
#include <atomic>
#include <event2/http.h>
#include <curl/curlver.h>
#include "../utils/map_extra.h"
#include "../utils/string.h"
#include "../version.h"
struct Request
{
@@ -36,11 +41,40 @@ struct listener_args
uint32_t looper_interval = 200;
};
void append_response(const std::string &method, const std::string &uri, const std::string &content_type, response_callback response);
void append_redirect(const std::string &uri, const std::string &target);
void reset_redirect();
int start_web_server(void *argv);
int start_web_server_multi(void *argv);
void stop_web_server();
struct responseRoute
{
std::string method;
std::string path;
std::string content_type;
response_callback rc;
};
class WebServer
{
public:
std::string user_agent_str = "subconverter/" VERSION " cURL/" LIBCURL_VERSION;
std::atomic_bool SERVER_EXIT_FLAG{false};
// file server
bool serve_file = false;
std::string serve_file_root;
// basic authentication
bool require_auth = false;
std::string auth_user, auth_password, auth_realm = "Please enter username and password:";
void append_response(const std::string &method, const std::string &uri, const std::string &content_type, response_callback response);
void append_redirect(const std::string &uri, const std::string &target);
void reset_redirect();
int start_web_server(void *argv);
int start_web_server_multi(void *argv);
void stop_web_server();
private:
int serveFile(const std::string &filename, std::string &content_type, std::string &return_data);
inline int process_request(Request &request, Response &response, std::string &return_data);
void on_request(evhttp_request *req, void *args);
std::vector<responseRoute> responses;
string_map redirect_map;
};
#endif // WEBSERVER_H_INCLUDED
+101 -82
View File
@@ -14,7 +14,9 @@
#include <string.h>
#include <pthread.h>
#include <thread>
#include <functional>
#include "../utils/base64/base64.h"
#include "../utils/file_extra.h"
#include "../utils/logger.h"
#include "../utils/stl_extra.h"
@@ -23,12 +25,22 @@
#include "socket.h"
#include "webserver.h"
extern std::string user_agent_str;
std::atomic_bool SERVER_EXIT_FLAG(false);
template <typename Lambda, class Ret, class... Args, class Pointer = Ret (*)(Args...)>
Pointer deduced_wrap(
const std::function<Ret(Args...)> &func)
{
static auto saved = func;
static Pointer p = [](Args... args) {
return saved(std::forward<Args>(args)...);
};
return p;
}
// file server
bool gServeFile = false;
std::string gServeFileRoot;
template <typename Lambda>
auto *wrap(Lambda &&func)
{
return deduced_wrap<Lambda>(std::function{func});
}
struct MIME_type
{
@@ -78,9 +90,9 @@ std::string checkMIMEType(const std::string &filename)
return "application/octet-stream";
}
int serveFile(const std::string &filename, std::string &content_type, std::string &return_data)
int WebServer::serveFile(const std::string &filename, std::string &content_type, std::string &return_data)
{
std::string realname = gServeFileRoot + filename;
std::string realname = serve_file_root + filename;
if(filename.compare("/") == 0)
realname += "index.html";
if(!fileExist(realname))
@@ -92,17 +104,6 @@ int serveFile(const std::string &filename, std::string &content_type, std::strin
return 0;
}
struct responseRoute
{
std::string method;
std::string path;
std::string content_type;
response_callback rc;
};
std::vector<responseRoute> responses;
string_map redirect_map;
const char *request_header_blacklist[] = {"host", "accept", "accept-encoding"};
static inline void buffer_cleanup(struct evbuffer *eb)
@@ -114,7 +115,7 @@ static inline void buffer_cleanup(struct evbuffer *eb)
#endif // MALLOC_TRIM
}
static inline int process_request(Request &request, Response &response, std::string &return_data)
inline int WebServer::process_request(Request &request, Response &response, std::string &return_data)
{
writeLog(0, "handle_cmd: " + request.method + " handle_uri: " + request.url, LOG_LEVEL_VERBOSE);
@@ -125,13 +126,17 @@ static inline int process_request(Request &request, Response &response, std::str
request.url.erase(pos);
}
if(request.method == "OPTIONS")
{
for(responseRoute &x : responses)
if(matchSpaceSeparatedList(replaceAllDistinct(request.postdata, ",", ""), x.method) && x.path == request.url)
return 1;
return -1;
}
for(responseRoute &x : responses)
{
if(request.method == "OPTIONS" && matchSpaceSeparatedList(replaceAllDistinct(request.postdata, ",", ""), x.method) && x.path == request.url)
{
return 1;
}
else if(x.method == request.method && x.path == request.url)
if(x.method == request.method && x.path == request.url)
{
response_callback &rc = x.rc;
return_data = rc(request, response);
@@ -151,11 +156,10 @@ static inline int process_request(Request &request, Response &response, std::str
else
return_data += "?" + request.argument;
}
response.content_type = "REDIRECT";
return 0;
return 2;
}
if(gServeFile)
if(serve_file)
{
if(request.method.compare("GET") == 0 && serveFile(request.url, response.content_type, return_data) == 0)
return 0;
@@ -164,9 +168,10 @@ static inline int process_request(Request &request, Response &response, std::str
return -1;
}
void OnReq(evhttp_request *req, void *args)
void WebServer::on_request(evhttp_request *req, void *args)
{
(void)args;
static std::string auth_token = "Basic " + base64Encode(auth_user + ":" + auth_password);
const char *req_content_type = evhttp_find_header(req->input_headers, "Content-Type"), *req_ac_method = evhttp_find_header(req->input_headers, "Access-Control-Request-Method");
const char *uri = req->uri, *internal_flag = evhttp_find_header(req->input_headers, "SubConverter-Request");
@@ -176,26 +181,41 @@ void OnReq(evhttp_request *req, void *args)
//std::cerr<<"Accept connection from client "<<client_ip<<":"<<client_port<<"\n";
writeLog(0, "Accept connection from client " + std::string(client_ip) + ":" + std::to_string(client_port), LOG_LEVEL_DEBUG);
if(internal_flag != NULL)
if (internal_flag != nullptr)
{
evhttp_send_error(req, 500, "Loop request detected!");
return;
}
if (require_auth)
{
const char *auth = evhttp_find_header(req->input_headers, "Authorization");
if (auth == nullptr || auth_token != auth)
{
evhttp_add_header(req->output_headers, "WWW-Authenticate", ("Basic realm=\"" + auth_realm + "\"").data());
auto buffer = evhttp_request_get_output_buffer(req);
evbuffer_add_printf(buffer, "Unauthorized");
evhttp_send_reply(req, 401, nullptr, buffer);
buffer_cleanup(buffer);
return;
}
}
Request request;
Response response;
if(EVBUFFER_LENGTH(req->input_buffer) != 0)
size_t buffer_len = evbuffer_get_length(req->input_buffer);
if (buffer_len != 0)
{
request.postdata.assign((char *)EVBUFFER_DATA(req->input_buffer), EVBUFFER_LENGTH(req->input_buffer));
if(req_content_type != NULL && strcmp(req_content_type, "application/x-www-form-urlencoded") == 0)
request.postdata.assign(reinterpret_cast<char*>(evbuffer_pullup(req->input_buffer, -1)), buffer_len);
if(req_content_type != nullptr && strcmp(req_content_type, "application/x-www-form-urlencoded") == 0)
request.postdata = urlDecode(request.postdata);
}
else if(req_ac_method != NULL)
else if (req_ac_method != nullptr)
{
request.postdata.assign(req_ac_method);
}
switch(req->type)
switch (req->type)
{
case EVHTTP_REQ_GET: request.method = "GET"; break;
case EVHTTP_REQ_POST: request.method = "POST"; break;
@@ -218,54 +238,50 @@ void OnReq(evhttp_request *req, void *args)
std::string return_data;
int retVal = process_request(request, response, return_data);
std::string content_type = response.content_type;
std::string &content_type = response.content_type;
auto *OutBuf = evhttp_request_get_output_buffer(req);
//struct evbuffer *OutBuf = evbuffer_new();
if (!OutBuf)
auto *output_buffer = evhttp_request_get_output_buffer(req);
if (!output_buffer)
{
evhttp_send_error(req, HTTP_INTERNAL, nullptr);
return;
}
for(auto &x : response.headers)
for (auto &x : response.headers)
evhttp_add_header(req->output_headers, x.first.data(), x.second.data());
switch(retVal)
switch (retVal)
{
case 1: //found OPTIONS
evhttp_add_header(req->output_headers, "Access-Control-Allow-Origin", "*");
evhttp_add_header(req->output_headers, "Access-Control-Allow-Headers", "*");
evhttp_send_reply(req, response.status_code, "", NULL);
evhttp_send_reply(req, response.status_code, nullptr, nullptr);
break;
case 2: //found redirect
evhttp_add_header(req->output_headers, "Location", return_data.c_str());
evhttp_send_reply(req, HTTP_MOVETEMP, nullptr, nullptr);
break;
case 0: //found normal
if(content_type.size())
{
if(content_type == "REDIRECT")
{
evhttp_add_header(req->output_headers, "Location", return_data.c_str());
evhttp_send_reply(req, HTTP_MOVETEMP, "", NULL);
buffer_cleanup(OutBuf);
return;
}
else
evhttp_add_header(req->output_headers, "Content-Type", content_type.c_str());
}
if (content_type.size())
evhttp_add_header(req->output_headers, "Content-Type", content_type.c_str());
evhttp_add_header(req->output_headers, "Access-Control-Allow-Origin", "*");
evhttp_add_header(req->output_headers, "Connection", "close");
evbuffer_add(OutBuf, return_data.data(), return_data.size());
evhttp_send_reply(req, response.status_code, "", OutBuf);
evbuffer_add(output_buffer, return_data.data(), return_data.size());
evhttp_send_reply(req, response.status_code, nullptr, output_buffer);
break;
case -1: //not found
return_data = "File not found.";
evbuffer_add(OutBuf, return_data.data(), return_data.size());
evhttp_send_reply(req, HTTP_NOTFOUND, "", OutBuf);
evbuffer_add(output_buffer, return_data.data(), return_data.size());
evhttp_send_reply(req, HTTP_NOTFOUND, nullptr, output_buffer);
//evhttp_send_error(req, HTTP_NOTFOUND, "Resource not found");
break;
default: //undefined behavior
evhttp_send_error(req, HTTP_INTERNAL, "");
evhttp_send_error(req, HTTP_INTERNAL, nullptr);
}
buffer_cleanup(OutBuf);
buffer_cleanup(output_buffer);
}
int start_web_server(void *argv)
int WebServer::start_web_server(void *argv)
{
struct listener_args *args = reinterpret_cast<listener_args*>(argv);
std::string listen_address = args->listen_address;
@@ -278,17 +294,19 @@ int start_web_server(void *argv)
}
const char *SrvAddress = listen_address.c_str();
std::uint16_t SrvPort = port;
std::unique_ptr<evhttp, decltype(&evhttp_free)> Server(evhttp_start(SrvAddress, SrvPort), &evhttp_free);
if (!Server)
std::unique_ptr<evhttp, decltype(&evhttp_free)> server(evhttp_start(SrvAddress, SrvPort), &evhttp_free);
if (!server)
{
//std::cerr << "Failed to init http server." << std::endl;
writeLog(0, "Failed to init http server.", LOG_LEVEL_FATAL);
return -1;
}
evhttp_set_allowed_methods(Server.get(), EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS | EVHTTP_REQ_PUT | EVHTTP_REQ_PATCH | EVHTTP_REQ_DELETE);
evhttp_set_gencb(Server.get(), OnReq, nullptr);
evhttp_set_timeout(Server.get(), 30);
auto call_on_request = [&](evhttp_request *req, void *args) { on_request(req, args); };
evhttp_set_allowed_methods(server.get(), EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS | EVHTTP_REQ_PUT | EVHTTP_REQ_PATCH | EVHTTP_REQ_DELETE);
evhttp_set_gencb(server.get(), wrap(call_on_request), nullptr);
evhttp_set_timeout(server.get(), 30);
if (event_dispatch() == -1)
{
//std::cerr << "Failed to run message loop." << std::endl;
@@ -303,7 +321,7 @@ void* httpserver_dispatch(void *arg)
{
event_base_dispatch(reinterpret_cast<event_base*>(arg));
event_base_free(reinterpret_cast<event_base*>(arg)); //free resources
return NULL;
return nullptr;
}
int httpserver_bindsocket(std::string listen_address, int listen_port, int backlog)
@@ -345,44 +363,45 @@ int httpserver_bindsocket(std::string listen_address, int listen_port, int backl
return nfd;
}
int start_web_server_multi(void *argv)
int WebServer::start_web_server_multi(void *argv)
{
struct listener_args *args = reinterpret_cast<listener_args*>(argv);
std::string listen_address = args->listen_address;
int port = args->port, nthreads = args->max_workers;
int i;
int port = args->port, nthreads = args->max_workers, max_conn = args->max_conn;
int nfd = httpserver_bindsocket(listen_address, port, args->max_conn);
auto call_on_request = [&](evhttp_request *req, void *args) { on_request(req, args); };
int nfd = httpserver_bindsocket(listen_address, port, max_conn);
if (nfd < 0)
return -1;
pthread_t ths[nthreads];
struct event_base *base[nthreads];
for (i = 0; i < nthreads; i++)
for (int i = 0; i < nthreads; i++)
{
base[i] = event_init();
if (base[i] == NULL)
if (base[i] == nullptr)
return -1;
struct evhttp *httpd = evhttp_new(base[i]);
if (httpd == NULL)
if (httpd == nullptr)
return -1;
if (evhttp_accept_socket(httpd, nfd) != 0)
return -1;
evhttp_set_allowed_methods(httpd, EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS);
evhttp_set_gencb(httpd, OnReq, nullptr);
evhttp_set_allowed_methods(httpd, EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS | EVHTTP_REQ_PUT | EVHTTP_REQ_PATCH | EVHTTP_REQ_DELETE);
evhttp_set_gencb(httpd, wrap(call_on_request), nullptr);
evhttp_set_timeout(httpd, 30);
if (pthread_create(&ths[i], NULL, httpserver_dispatch, base[i]) != 0)
if (pthread_create(&ths[i], nullptr, httpserver_dispatch, base[i]) != 0)
return -1;
}
while (!SERVER_EXIT_FLAG)
{
if(args->looper_callback != nullptr)
if (args->looper_callback != nullptr)
args->looper_callback();
std::this_thread::sleep_for(std::chrono::milliseconds(args->looper_interval)); //block forever until receive stop signal
}
for (i = 0; i < nthreads; i++)
for (int i = 0; i < nthreads; i++)
event_base_loopbreak(base[i]); //stop the loop
shutdown(nfd, SD_BOTH); //stop accept call
@@ -391,12 +410,12 @@ int start_web_server_multi(void *argv)
return 0;
}
void stop_web_server()
void WebServer::stop_web_server()
{
SERVER_EXIT_FLAG = true;
}
void append_response(const std::string &method, const std::string &uri, const std::string &content_type, response_callback response)
void WebServer::append_response(const std::string &method, const std::string &uri, const std::string &content_type, response_callback response)
{
responseRoute rr;
rr.method = method;
@@ -406,12 +425,12 @@ void append_response(const std::string &method, const std::string &uri, const st
responses.emplace_back(std::move(rr));
}
void append_redirect(const std::string &uri, const std::string &target)
void WebServer::append_redirect(const std::string &uri, const std::string &target)
{
redirect_map[uri] = target;
}
void reset_redirect()
void WebServer::reset_redirect()
{
eraseElements(redirect_map);
}
+2
View File
@@ -34,6 +34,8 @@ public:
return *this;
}
inline bool operator==(const tribool& rhs){ return _M_VALUE == rhs._M_VALUE; }
operator bool() const { return _M_VALUE == 3; }
bool is_undef() const { return _M_VALUE <= 1; }