Fix request arguments not properly handled (#661)

This commit is contained in:
Tindy X committed 2023-11-09 15:02:24 +08:00
1 parent 833dee1dbc
commit c530bc5eaa
12 files changed
+177 -543

No files matched your search

+88 -91
View File
@@ -37,8 +37,6 @@ extern WebServer webServer;
string_array gRegexBlacklist = {"(.*)*"};
void refreshRulesets(RulesetConfigs &ruleset_list, std::vector<RulesetContent> &ruleset_content_array);
std::string parseProxy(const std::string &source)
{
std::string proxy = source;
@@ -136,19 +134,18 @@ void matchUserAgent(const std::string &user_agent, std::string &target, tribool
return;
}
}
return;
}
std::string getRuleset(RESPONSE_CALLBACK_ARGS)
{
std::string &argument = request.argument;
auto &argument = request.argument;
int *status_code = &response.status_code;
/// type: 1 for Surge, 2 for Quantumult X, 3 for Clash domain rule-provider, 4 for Clash ipcidr rule-provider, 5 for Surge DOMAIN-SET, 6 for Clash classical ruleset
std::string url = urlSafeBase64Decode(getUrlArg(argument, "url")), type = getUrlArg(argument, "type"), group = urlSafeBase64Decode(getUrlArg(argument, "group"));
std::string output_content, dummy;
int type_int = to_int(type, 0);
if(!url.size() || !type.size() || (type_int == 2 && !group.size()) || (type_int < 1 || type_int > 6))
if(url.empty() || type.empty() || (type_int == 2 && group.empty()) || (type_int < 1 || type_int > 6))
{
*status_code = 400;
return "Invalid request!";
@@ -167,7 +164,7 @@ std::string getRuleset(RESPONSE_CALLBACK_ARGS)
output_content += convertRuleset(content, x.rule_type);
}
if(!output_content.size())
if(output_content.empty())
{
*status_code = 400;
return "Invalid request!";
@@ -184,11 +181,11 @@ std::string getRuleset(RESPONSE_CALLBACK_ARGS)
{
posb = 0;
pose = strLine.find(',');
if(pose == strLine.npos)
if(pose == std::string::npos)
return 1;
posb = pose + 1;
pose = strLine.find(',', posb);
if(pose == strLine.npos)
if(pose == std::string::npos)
{
pose = strLine.size();
if(strLine[pose - 1] == '\r')
@@ -215,11 +212,11 @@ std::string getRuleset(RESPONSE_CALLBACK_ARGS)
switch(type_int)
{
case 2:
if(!std::any_of(QuanXRuleTypes.begin(), QuanXRuleTypes.end(), [&strLine](std::string type){return startsWith(strLine, type);}))
if(!std::any_of(QuanXRuleTypes.begin(), QuanXRuleTypes.end(), [&strLine](const std::string& type){return startsWith(strLine, type);}))
continue;
break;
case 1:
if(!std::any_of(SurgeRuleTypes.begin(), SurgeRuleTypes.end(), [&strLine](std::string type){return startsWith(strLine, type);}))
if(!std::any_of(SurgeRuleTypes.begin(), SurgeRuleTypes.end(), [&strLine](const std::string& type){return startsWith(strLine, type);}))
continue;
break;
case 3:
@@ -251,7 +248,7 @@ std::string getRuleset(RESPONSE_CALLBACK_ARGS)
output_content += '\n';
continue;
case 6:
if(!std::any_of(ClashRuleTypes.begin(), ClashRuleTypes.end(), [&strLine](std::string type){return startsWith(strLine, type);}))
if(!std::any_of(ClashRuleTypes.begin(), ClashRuleTypes.end(), [&strLine](const std::string& type){return startsWith(strLine, type);}))
continue;
output_content += " - ";
}
@@ -303,12 +300,12 @@ void checkExternalBase(const std::string &path, std::string &dest)
std::string subconverter(RESPONSE_CALLBACK_ARGS)
{
std::string &argument = request.argument;
auto &argument = request.argument;
int *status_code = &response.status_code;
std::string argTarget = getUrlArg(argument, "target"), argSurgeVer = getUrlArg(argument, "ver");
tribool argClashNewField = getUrlArg(argument, "new_name");
int intSurgeVer = argSurgeVer.size() ? to_int(argSurgeVer, 3) : 3;
int intSurgeVer = !argSurgeVer.empty() ? to_int(argSurgeVer, 3) : 3;
if(argTarget == "auto")
matchUserAgent(request.headers["User-Agent"], argTarget, argClashNewField, intSurgeVer);
@@ -352,8 +349,8 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
std::vector<RulesetContent> lRulesetContent;
extra_settings ext;
std::string subInfo, dummy;
int interval = argUpdateInterval.size() ? to_int(argUpdateInterval, global.updateInterval) : global.updateInterval;
bool authorized = !global.APIMode || getUrlArg(argument, "token") == global.accessToken, strict = argUpdateStrict.size() ? argUpdateStrict == "true" : global.updateStrict;
int interval = !argUpdateInterval.empty() ? to_int(argUpdateInterval, global.updateInterval) : global.updateInterval;
bool authorized = !global.APIMode || getUrlArg(argument, "token") == global.accessToken, strict = !argUpdateStrict.empty() ? argUpdateStrict == "true" : global.updateStrict;
if(std::find(gRegexBlacklist.cbegin(), gRegexBlacklist.cend(), argIncludeRemark) != gRegexBlacklist.cend() || std::find(gRegexBlacklist.cbegin(), gRegexBlacklist.cend(), argExcludeRemark) != gRegexBlacklist.cend())
return "Invalid request!";
@@ -365,28 +362,35 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
/// validate urls
argEnableInsert.define(global.enableInsert);
if(!argUrl.size() && (!global.APIMode || authorized))
if(argUrl.empty() && (!global.APIMode || authorized))
argUrl = global.defaultUrls;
if((!argUrl.size() && !(global.insertUrls.size() && argEnableInsert)) || !argTarget.size())
if((argUrl.empty() && !(!global.insertUrls.empty() && argEnableInsert)) || argTarget.empty())
{
*status_code = 400;
return "Invalid request!";
}
/// load request arguments as template variables
string_array req_args = split(argument, "&");
// string_array req_args = split(argument, "&");
// string_map req_arg_map;
// for(std::string &x : req_args)
// {
// string_size pos = x.find("=");
// if(pos == x.npos)
// {
// req_arg_map[x] = "";
// continue;
// }
// if(x.substr(0, pos) == "token")
// continue;
// req_arg_map[x.substr(0, pos)] = x.substr(pos + 1);
// }
string_map req_arg_map;
for(std::string &x : req_args)
for (auto &x : argument)
{
string_size pos = x.find("=");
if(pos == x.npos)
{
req_arg_map[x] = "";
if(x.first == "token")
continue;
}
if(x.substr(0, pos) == "token")
continue;
req_arg_map[x.substr(0, pos)] = x.substr(pos + 1);
req_arg_map[x.first] = x.second;
}
req_arg_map["target"] = argTarget;
req_arg_map["ver"] = std::to_string(intSurgeVer);
@@ -414,7 +418,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
ext.tls13.define(argTLS13).define(global.TLS13Flag);
ext.sort_flag = argSort.get(global.enableSort);
argUseSortScript.define(global.sortScript.size() != 0);
argUseSortScript.define(!global.sortScript.empty());
if(ext.sort_flag && argUseSortScript)
ext.sort_script = global.sortScript;
ext.filter_deprecated = argFilterDeprecated.get(global.filterDeprecated);
@@ -428,7 +432,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
ext.nodelist = argGenNodeList;
ext.surge_ssr_path = global.surgeSSRPath;
ext.quanx_dev_id = argDeviceID.size() ? argDeviceID : global.quanXDevID;
ext.quanx_dev_id = !argDeviceID.empty() ? argDeviceID : global.quanXDevID;
ext.enable_rule_generator = global.enableRuleGen;
ext.overwrite_original_rules = global.overwriteOriginalRules;
if(!argExpandRulesets)
@@ -437,7 +441,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
/// load external configuration
if(argExternalConfig.empty())
argExternalConfig = global.defaultExtConfig;
if(argExternalConfig.size())
if(!argExternalConfig.empty())
{
//std::cerr<<"External configuration file provided. Loading...\n";
writeLog(0, "External configuration file provided. Loading...", LOG_LEVEL_INFO);
@@ -459,21 +463,21 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
checkExternalBase(extconf.loon_rule_base, lLoonBase);
checkExternalBase(extconf.singbox_rule_base, lSingBoxBase);
if(extconf.surge_ruleset.size())
if(!extconf.surge_ruleset.empty())
lCustomRulesets = extconf.surge_ruleset;
if(extconf.custom_proxy_group.size())
if(!extconf.custom_proxy_group.empty())
lCustomProxyGroups = extconf.custom_proxy_group;
ext.enable_rule_generator = extconf.enable_rule_generator;
ext.overwrite_original_rules = extconf.overwrite_original_rules;
}
}
if(extconf.rename.size())
if(!extconf.rename.empty())
ext.rename_array = extconf.rename;
if(extconf.emoji.size())
if(!extconf.emoji.empty())
ext.emoji_array = extconf.emoji;
if(extconf.include.size())
if(!extconf.include.empty())
lIncludeRemarks = extconf.include;
if(extconf.exclude.size())
if(!extconf.exclude.empty())
lExcludeRemarks = extconf.exclude;
argAddEmoji.define(extconf.add_emoji);
argRemoveEmoji.define(extconf.remove_old_emoji);
@@ -484,14 +488,14 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
if(!lSimpleSubscription)
{
/// loading custom groups
if(argCustomGroups.size() && !ext.nodelist)
if(!argCustomGroups.empty() && !ext.nodelist)
{
string_array vArray = split(argCustomGroups, "@");
lCustomProxyGroups = INIBinding::from<ProxyGroupConfig>::from_ini(vArray);
}
/// loading custom rulesets
if(argCustomRulesets.size() && !ext.nodelist)
if(!argCustomRulesets.empty() && !ext.nodelist)
{
string_array vArray = split(argCustomRulesets, "@");
lCustomRulesets = INIBinding::from<RulesetConfig>::from_ini(vArray);
@@ -519,15 +523,15 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
ext.remove_emoji = argRemoveEmoji.get(global.removeEmoji);
if(ext.add_emoji && ext.emoji_array.empty())
ext.emoji_array = safe_get_emojis();
if(argRenames.size())
if(!argRenames.empty())
ext.rename_array = INIBinding::from<RegexMatchConfig>::from_ini(split(argRenames, "`"), "@");
else if(ext.rename_array.empty())
ext.rename_array = safe_get_renames();
/// check custom include/exclude settings
if(argIncludeRemark.size() && regValid(argIncludeRemark))
if(!argIncludeRemark.empty() && regValid(argIncludeRemark))
lIncludeRemarks = string_array{argIncludeRemark};
if(argExcludeRemark.size() && regValid(argExcludeRemark))
if(!argExcludeRemark.empty() && regValid(argExcludeRemark))
lExcludeRemarks = string_array{argExcludeRemark};
/// initialize script runtime
@@ -559,7 +563,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
parse_set.js_runtime = ext.js_runtime;
parse_set.js_context = ext.js_context;
if(global.insertUrls.size() && argEnableInsert)
if(!global.insertUrls.empty() && argEnableInsert)
{
groupID = -1;
urls = split(global.insertUrls, "|");
@@ -602,12 +606,12 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
groupID++;
}
//exit if found nothing
if(!nodes.size() && !insert_nodes.size())
if(nodes.empty() && insert_nodes.empty())
{
*status_code = 400;
return "No nodes were found!";
}
if(subInfo.size() && argAppendUserinfo.get(global.appendUserinfo))
if(!subInfo.empty() && argAppendUserinfo.get(global.appendUserinfo))
response.headers.emplace("Subscription-UserInfo", subInfo);
if(request.method == "HEAD")
@@ -627,7 +631,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
std::string filterScript = global.filterScript;
if(authorized && !argFilterScript.empty())
filterScript = argFilterScript;
if(filterScript.size())
if(!filterScript.empty())
{
if(startsWith(filterScript, "path:"))
filterScript = fileGet(filterScript.substr(5), false);
@@ -670,7 +674,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
}
//check custom group name
if(argGroupName.size())
if(!argGroupName.empty())
for(Proxy &x : nodes)
x.Group = argGroupName;
@@ -695,7 +699,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
std::vector<RulesetContent> dummy_ruleset;
std::string managed_url = base64Decode(urlDecode(getUrlArg(argument, "profile_data")));
if(managed_url.empty())
managed_url = global.managedConfigPrefix + "/sub?" + argument;
managed_url = global.managedConfigPrefix + "/sub?" + joinArguments(argument);
//std::cerr<<"Generate target: ";
proxy = parseProxy(global.proxyConfig);
@@ -747,7 +751,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
if(argUpload)
uploadGist("surge" + argSurgeVer, argUploadPath, output_content, true);
if(global.writeManagedConfig && global.managedConfigPrefix.size())
if(global.writeManagedConfig && !global.managedConfigPrefix.empty())
output_content = "#!MANAGED-CONFIG " + managed_url + (interval ? " interval=" + std::to_string(interval) : "") \
+ " strict=" + std::string(strict ? "true" : "false") + "\n\n" + output_content;
}
@@ -764,7 +768,7 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
if(argUpload)
uploadGist("surfboard", argUploadPath, output_content, true);
if(global.writeManagedConfig && global.managedConfigPrefix.size())
if(global.writeManagedConfig && !global.managedConfigPrefix.empty())
output_content = "#!MANAGED-CONFIG " + managed_url + (interval ? " interval=" + std::to_string(interval) : "") \
+ " strict=" + std::string(strict ? "true" : "false") + "\n\n" + output_content;
break;
@@ -899,18 +903,18 @@ std::string subconverter(RESPONSE_CALLBACK_ARGS)
return "Unrecognized target";
}
writeLog(0, "Generate completed.", LOG_LEVEL_INFO);
if(argFilename.size())
if(!argFilename.empty())
response.headers.emplace("Content-Disposition", "attachment; filename=\"" + argFilename + "\"; filename*=utf-8''" + urlEncode(argFilename));
return output_content;
}
std::string simpleToClashR(RESPONSE_CALLBACK_ARGS)
{
std::string &argument = request.argument;
auto argument = joinArguments(request.argument);
int *status_code = &response.status_code;
std::string url = argument.size() <= 8 ? "" : argument.substr(8);
if(!url.size() || argument.substr(0, 8) != "sublink=")
if(url.empty() || argument.substr(0, 8) != "sublink=")
{
*status_code = 400;
return "Invalid request!";
@@ -920,13 +924,14 @@ std::string simpleToClashR(RESPONSE_CALLBACK_ARGS)
*status_code = 400;
return "Please insert your subscription link instead of clicking the default link.";
}
request.argument = "target=clashr&url=" + urlEncode(url);
request.argument.emplace("target", "clashr");
request.argument.emplace("url", urlEncode(url));
return subconverter(request, response);
}
std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
{
std::string &argument = request.argument;
auto argument = joinArguments(request.argument);
int *status_code = &response.status_code;
INIReader ini;
@@ -937,9 +942,9 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
ini.store_any_line = true;
if(!url.size())
if(url.empty())
url = global.defaultUrls;
if(!url.size() || argument.substr(0, 5) != "link=")
if(url.empty() || argument.substr(0, 5) != "link=")
{
*status_code = 400;
return "Invalid request!";
@@ -1008,11 +1013,11 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
for(unsigned int i = 1; i < dummy_str_array.size(); i++)
{
if(startsWith(dummy_str_array[i], "url"))
singlegroup["url"] = trim(dummy_str_array[i].substr(dummy_str_array[i].find("=") + 1));
singlegroup["url"] = trim(dummy_str_array[i].substr(dummy_str_array[i].find('=') + 1));
else if(startsWith(dummy_str_array[i], "interval"))
singlegroup["interval"] = trim(dummy_str_array[i].substr(dummy_str_array[i].find("=") + 1));
singlegroup["interval"] = trim(dummy_str_array[i].substr(dummy_str_array[i].find('=') + 1));
else if(startsWith(dummy_str_array[i], "policy-path"))
links.emplace_back(trim(dummy_str_array[i].substr(dummy_str_array[i].find("=") + 1)));
links.emplace_back(trim(dummy_str_array[i].substr(dummy_str_array[i].find('=') + 1)));
else
singlegroup["proxies"].push_back(trim(dummy_str_array[i]));
}
@@ -1048,7 +1053,7 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
}
//exit if found nothing
if(!nodes.size())
if(nodes.empty())
{
*status_code = 400;
return "No nodes were found!";
@@ -1075,7 +1080,7 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
name = x.first;
content = x.second;
dummy_str_array = split(content, ",");
if(!dummy_str_array.size())
if(dummy_str_array.empty())
continue;
content = trim(dummy_str_array[0]);
switch(hash_(content))
@@ -1112,7 +1117,7 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
if(strArray.size() != 3)
continue;
content = webGet(strArray[1], proxy, global.cacheRuleset);
if(!content.size())
if(content.empty())
continue;
ss << content;
@@ -1125,7 +1130,7 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
strLine.erase(--lineSize);
if(!lineSize || strLine[0] == ';' || strLine[0] == '#' || (lineSize >= 2 && strLine[0] == '/' && strLine[1] == '/')) //empty lines and comments are ignored
continue;
else if(!std::any_of(ClashRuleTypes.begin(), ClashRuleTypes.end(), [&strLine](std::string type){return startsWith(strLine, type);})) //remove unsupported types
else if(!std::any_of(ClashRuleTypes.begin(), ClashRuleTypes.end(), [&strLine](const std::string& type){return startsWith(strLine, type);})) //remove unsupported types
continue;
strLine += strArray[2];
if(count_least(strLine, ',', 3))
@@ -1135,7 +1140,7 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
ss.clear();
continue;
}
else if(!std::any_of(ClashRuleTypes.begin(), ClashRuleTypes.end(), [&strLine](std::string type){return startsWith(strLine, type);}))
else if(!std::any_of(ClashRuleTypes.begin(), ClashRuleTypes.end(), [&strLine](const std::string& type){return startsWith(strLine, type);}))
continue;
rule.push_back(x);
}
@@ -1148,7 +1153,7 @@ std::string surgeConfToClash(RESPONSE_CALLBACK_ARGS)
std::string getProfile(RESPONSE_CALLBACK_ARGS)
{
std::string &argument = request.argument;
auto &argument = request.argument;
int *status_code = &response.status_code;
std::string name = urlDecode(getUrlArg(argument, "name")), token = urlDecode(getUrlArg(argument, "token"));
@@ -1164,7 +1169,8 @@ std::string getProfile(RESPONSE_CALLBACK_ARGS)
{
profile_content = vfs::vfs_get(name);
}
else */if(fileExist(name))
else */
if(fileExist(name))
{
profile_content = fileGet(name, true);
}
@@ -1187,7 +1193,7 @@ std::string getProfile(RESPONSE_CALLBACK_ARGS)
writeLog(0, "Trying to parse profile '" + name + "'.", LOG_LEVEL_INFO);
string_multimap contents;
ini.get_items("Profile", contents);
if(!contents.size())
if(contents.empty())
{
//std::cerr<<"Load profile failed! Reason: Empty Profile section\n";
writeLog(0, "Load profile failed! Reason: Empty Profile section", LOG_LEVEL_ERROR);
@@ -1234,7 +1240,7 @@ std::string getProfile(RESPONSE_CALLBACK_ARGS)
continue;
}
url = ini.get("Profile", "url");
if(url.size())
if(!url.empty())
{
all_urls += "|" + url;
writeLog(0, "Profile url from '" + name + "' added.", LOG_LEVEL_INFO);
@@ -1248,10 +1254,8 @@ std::string getProfile(RESPONSE_CALLBACK_ARGS)
}
contents.emplace("token", token);
contents.emplace("profile_data", base64Encode(global.managedConfigPrefix + "/getprofile?" + argument));
std::string query = std::accumulate(contents.begin(), contents.end(), std::string(), [](const std::string &x, auto y){ return x + y.first + "=" + urlEncode(y.second) + "&"; });
query += argument;
request.argument = query;
contents.emplace("profile_data", base64Encode(global.managedConfigPrefix + "/getprofile?" + joinArguments(argument)));
request.argument = argument;
return subconverter(request, response);
}
@@ -1276,7 +1280,7 @@ inline std::string intToStream(unsigned long long stream)
streamval /= 1024.0;
}
snprintf(chrs, 15, "%.2f %cB", streamval, units[level]);
return std::string(chrs);
return {chrs};
}
std::string subInfoToMessage(std::string subinfo)
@@ -1286,7 +1290,7 @@ std::string subInfoToMessage(std::string subinfo)
std::string retdata, useddata = "N/A", totaldata = "N/A", expirydata = "N/A";
std::string upload = getUrlArg(subinfo, "upload"), download = getUrlArg(subinfo, "download"), total = getUrlArg(subinfo, "total"), expire = getUrlArg(subinfo, "expire");
ull used = to_number<ull>(upload, 0) + to_number<ull>(download, 0), tot = to_number<ull>(total, 0);
time_t expiry = to_number<time_t>(expire, 0);
auto expiry = to_number<time_t>(expire, 0);
if(used != 0)
useddata = intToStream(used);
if(tot != 0)
@@ -1309,7 +1313,7 @@ int simpleGenerator()
{
//std::cerr<<"\nReading generator configuration...\n";
writeLog(0, "Reading generator configuration...", LOG_LEVEL_INFO);
std::string config = fileGet("generate.ini"), path, profile, arguments, content;
std::string config = fileGet("generate.ini"), path, profile, content;
if(config.empty())
{
//std::cerr<<"Generator configuration not found or empty!\n";
@@ -1328,7 +1332,7 @@ int simpleGenerator()
writeLog(0, "Read generator configuration completed.\n", LOG_LEVEL_INFO);
string_array sections = ini.get_section_names();
if(global.generateProfiles.size())
if(!global.generateProfiles.empty())
{
//std::cerr<<"Generating with specific artifacts: \""<<gen_profile<<"\"...\n";
writeLog(0, "Generating with specific artifacts: \"" + global.generateProfiles + "\"...", LOG_LEVEL_INFO);
@@ -1358,7 +1362,6 @@ int simpleGenerator()
Response response;
for(std::string &x : sections)
{
arguments.clear();
response.status_code = 200;
//std::cerr<<"Generating artifact '"<<x<<"'...\n";
writeLog(0, "Generating artifact '" + x + "'...", LOG_LEVEL_INFO);
@@ -1374,12 +1377,14 @@ int simpleGenerator()
if(ini.item_exist("profile"))
{
profile = ini.get("profile");
request.argument = "name=" + urlEncode(profile) + "&token=" + global.accessToken + "&expand=true";
request.argument.emplace("name", urlEncode(profile));
request.argument.emplace("token", global.accessToken);
request.argument.emplace("expand", "true");
content = getProfile(request, response);
}
else
{
if(ini.get_bool("direct") == true)
if(ini.get_bool("direct"))
{
std::string url = ini.get("url");
content = fetchFile(url, proxy, global.cacheSubscription);
@@ -1400,10 +1405,8 @@ int simpleGenerator()
{
if(y.first == "path")
continue;
arguments += y.first + "=" + urlEncode(y.second) + "&";
request.argument.emplace(y.first, y.second);
}
arguments.erase(arguments.size() - 1);
request.argument = arguments;
content = subconverter(request, response);
}
if(response.status_code != 200)
@@ -1429,7 +1432,7 @@ int simpleGenerator()
std::string renderTemplate(RESPONSE_CALLBACK_ARGS)
{
std::string &argument = request.argument;
auto &argument = request.argument;
int *status_code = &response.status_code;
std::string path = urlDecode(getUrlArg(argument, "path"));
@@ -1450,16 +1453,10 @@ std::string renderTemplate(RESPONSE_CALLBACK_ARGS)
tpl_args.global_vars = global.templateVars;
//load request arguments as template variables
string_array req_args = split(argument, "&");
string_size pos;
string_map req_arg_map;
for(std::string &x : req_args)
for (auto &x : argument)
{
pos = x.find("=");
if(pos == x.npos)
req_arg_map[x] = "";
else
req_arg_map[x.substr(0, pos)] = x.substr(pos + 1);
req_arg_map[x.first] = x.second;
}
tpl_args.request_params = req_arg_map;
+1 -1
View File
@@ -73,7 +73,7 @@ void refresh_schedule()
std::string list_cron_schedule(RESPONSE_CALLBACK_ARGS)
{
std::string &argument = request.argument;
auto &argument = request.argument;
std::string token = getUrlArg(argument, "token");
rapidjson::StringBuffer sb;
rapidjson::Writer<rapidjson::StringBuffer> writer(sb);
+6 -1
View File
@@ -377,6 +377,11 @@ static qjs_fetch_Response qjs_fetch(qjs_fetch_Request request)
return response;
}
static std::string qjs_getUrlArg(const std::string &url, const std::string &request)
{
return getUrlArg(url, request);
}
std::string getGeoIP(const std::string &address, const std::string &proxy)
{
return fetchFile("https://api.ip.sb/geoip/" + address, parseProxy(proxy), global.cacheConfig);
@@ -506,7 +511,7 @@ int script_context_init(qjs::Context &context)
.add<&currentTime>("time")
.add<&sleepMs>("sleep")
.add<&ShowMsgbox>("msgbox")
.add<&getUrlArg>("getUrlArg")
.add<&qjs_getUrlArg>("getUrlArg")
.add<&fileGet>("fileGet")
.add<&fileWrite>("fileWrite");
context.eval(R"(
-372
View File
@@ -1,372 +0,0 @@
#include <iostream>
#include <fstream>
#include <map>
#include <atomic>
#include <thread>
#include <pthread.h>
#include "../utils/file.h"
#include "../utils/string.h"
#include "../utils/system.h"
#include "socket.h"
#include "webserver.h"
using string_array = std::vector<std::string>;
int def_timeout = 5;
struct responseRoute
{
std::string method;
std::string path;
std::string content_type;
response_callback rc;
};
std::vector<responseRoute> responses;
//for use of multi-thread
int max_send_failure = 10;
std::atomic_bool SERVER_EXIT_FLAG(false);
std::atomic_int working_thread(0);
int sendall(SOCKET sock, std::string data)
{
unsigned int total = 0, bytesleft = data.size();
int sent = 0;
const char* datastr = data.data();
while(total < bytesleft)
{
sent = send(sock, datastr + total, bytesleft, 0);
if(sent < 0)
{
std::cerr<<strerror(errno)<<std::endl;
if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
{
continue;
}
else
{
break;
}
}
total += sent;
bytesleft -= sent;
}
return sent == -1 ? -1 : 0;
}
void wrong_req(SOCKET sock)
{
std::string response = "HTTP/1.1 501 Not Implemented\r\n"
"Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: *\r\n"
"Content-Type: text/plain\r\n\r\n"
"The command is not yet completed\r\n";
if (sendall(sock, response) == -1)
{
std::cerr << "Sending failed!" << std::endl;
}
}
void file_not_found(std::string arguments, int sock)
{
std::string prompt_info = "Not found: " + arguments;
std::string response = "HTTP/1.1 404 Not Found\r\n"
"Content-Type: text/plain\r\nConnection: close\r\n"
"Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: *\r\n"
"Content-Length: " + std::to_string(prompt_info.size()) + "\r\n\r\n" + prompt_info + "\r\n";
if (sendall(sock, response) == -1)
{
printf("Sending error!");
return;
}
}
void send_header(SOCKET send_to, std::string content_type)
{
std::string header = "HTTP/1.1 200 OK\r\nConnection: close\r\nCache-Control: no-cache, no-store, must-revalidate\r\nAccess-Control-Allow-Origin: *\r\n";
if(content_type.size())
header += "Content-Type: " + content_type + "\r\n";
if(sendall(send_to, header) == -1)
{
printf("Sending error!");
}
}
void send_options_header(SOCKET send_to)
{
std::string header = "HTTP/1.1 200 OK\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: *\r\n";
if(sendall(send_to, header) == -1)
{
printf("Sending error!");
}
}
char* file_type(const char* arg)
{
char * temp;
if ((temp=strrchr(arg,'.')) != NULL)
{
return temp+1;
}
return nullptr;
}
void serve_options(SOCKET sock)
{
send_options_header(sock);
std::string extra_header = "Content-Length: 0\r\n\r\n";
sendall(sock, extra_header);
sendall(sock, "\r\n\r\n");
}
void serve_content(SOCKET sock, std::string type, std::string content)
{
send_header(sock, type.data());
std::string extra_header = "Content-Length: " + std::to_string(content.size()) + "\r\n";
sendall(sock, extra_header);
send(sock, "\r\n", 2, 0);
if (sendall(sock, content) == -1)
{
printf("Sending error!");
}
sendall(sock, "\r\n\r\n");
}
void send_file(std::string arguments, int sock)
{
char* extension = file_type(arguments.data());
std::string content_type = "text/plain", data;
char sizestr[16] = {};
int len;
if (strcmp(extension, "html") == 0)
{
content_type = "text/html";
}
if (strcmp(extension, "gif") == 0)
{
content_type = "image/gif";
}
if (strcmp(extension, "jpg") == 0)
{
content_type = "image/jpg";
}
send_header(sock, content_type);
sendall(sock, "Transfer-Encoding: chunked\r\n\r\n");
data = fileGet(arguments);
len = data.size();
sprintf(sizestr, "%x\r\n", len);
if (sendall(sock, sizestr) == -1)
{
printf("Sending error!");
}
if (sendall(sock, data) == -1)
{
printf("Sending error!");
}
len = 2;
sendall(sock, "\r\n");
len = 7;
sendall(sock, "0\r\n\r\n");
}
int setTimeout(SOCKET s, int timeout)
{
int ret = -1;
#ifdef _WIN32
ret = setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(int));
ret = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(int));
#else
struct timeval timeo = {timeout / 1000, (timeout % 1000) * 1000};
ret = setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeo, sizeof(timeo));
ret = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeo, sizeof(timeo));
#endif
def_timeout = timeout;
return ret;
}
void handle_req(std::string request, int client_sock)
{
working_thread++;
std::cerr<<"worker startup"<<std::endl;
string_array vArray;
char command[16] = {};
char arguments[BUFSIZ] = {};
std::string uri, args, target, postdata;
if (sscanf(request.data(), "%s%s", command, arguments) != 2)
{
goto end;
}
std::cerr<<"handle_cmd: "<<command<<"\n"<<"handle_path: "<<arguments<<"\n";
vArray = split(arguments, "?");
if(vArray.size() > 2)
{
wrong_req(client_sock);
goto end;
}
else if(vArray.size() > 1)
{
uri = vArray[0];
args = vArray[1];
}
else
uri = arguments;
if(strcmp(command, "POST") == 0)
{
if(request.find("\r\n\r\n") != request.npos)
postdata = request.substr(request.find("\r\n\r\n") + 4);
}
else if(strcmp(command, "OPTIONS") == 0)
{
serve_options(client_sock);
goto end;
}
for(std::vector<responseRoute>::iterator iter = responses.begin(); iter != responses.end(); ++iter)
{
if(iter->method.compare(command) == 0 && iter->path == uri)
{
response_callback &rc = iter->rc;
serve_content(client_sock, iter->content_type, rc(args, postdata));
goto end;
}
}
file_not_found(uri, client_sock);
end:
std::cerr<<"worker stop"<<std::endl;
sleepMs(1);
closesocket(client_sock);
working_thread--;
}
void append_response(const std::string &type, const std::string &request, const std::string &content_type, response_callback response)
{
responseRoute rr;
rr.method = type;
rr.path = request;
rr.content_type = content_type;
rr.rc = response;
responses.push_back(std::move(rr));
}
void stop_web_server()
{
SERVER_EXIT_FLAG = true;
}
int start_web_server(void *argv)
{
struct listener_args *args = (listener_args*)argv;
args->max_workers = 1;
return start_web_server_multi(args);
}
int start_web_server_multi(void *argv)
{
//log startup
struct listener_args *args = (listener_args*)argv;
std::string listen_address = args->listen_address, request;
int port = args->port, max_conn = args->max_conn, max_workers = args->max_workers, numbytes, worker_index = 0;
socklen_t sock_size = sizeof(struct sockaddr_in);
char buf[BUFSIZ];
struct sockaddr_in user_socket, server_addr;
SOCKET acc_socket;
int server_socket, fail_counter = 0;
server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
std::thread workers[max_workers];
if (server_socket == -1)
{
//log socket error
std::cerr<<"socket build error!"<<std::endl;
return 0;
}
ZeroMemory(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons((short)port);
server_addr.sin_addr.s_addr = inet_addr(listen_address.data());
if (::bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
{
//log bind error
std::cerr<<"socket bind error!"<<std::endl;
closesocket(server_socket);
return 0;
}
if (listen(server_socket, max_conn) == -1 )
{
//log listen error
std::cerr<<"socket listen error!"<<std::endl;
closesocket(server_socket);
return 0;
}
setTimeout(server_socket, 500);
while(true)
{
acc_socket = accept(server_socket, (struct sockaddr *)&user_socket, &sock_size); //wait for connection
if(acc_socket < 0)
{
if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
{
fail_counter++;
if(fail_counter > max_send_failure)
break;
continue;
}
else
{
break;
}
}
request = "";
while(true) //receive the complete request
{
numbytes = recv(acc_socket, buf, BUFSIZ - 1, 0);
if(numbytes > 0) //received
request.append(buf);
if(numbytes < 0)
{
if(errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
{
continue;
}
else
{
break;
}
}
if(numbytes == 0)
break;
}
//handle_req(buf, acc_socket);
while(working_thread >= max_workers)
{
sleepMs(10);
if(SERVER_EXIT_FLAG)
break;
}
while(workers[worker_index].get_id() != std::thread::id())
{
worker_index++;
}
workers[worker_index] = std::thread(handle_req, request, acc_socket);
workers[worker_index].detach();
worker_index++;
if(worker_index > max_workers)
worker_index = 0;
}
return 0;
}
+25 -7
View File
@@ -14,7 +14,7 @@ struct Request
{
std::string method;
std::string url;
std::string argument;
string_multimap argument;
string_icase_map headers;
std::string postdata;
};
@@ -45,7 +45,7 @@ struct responseRoute
std::string method;
std::string path;
std::string content_type;
response_callback rc;
response_callback rc {};
};
class WebServer
@@ -62,13 +62,31 @@ public:
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();
void append_response(const std::string &method, const std::string &uri, const std::string &content_type, response_callback response)
{
responseRoute rr;
rr.method = method;
rr.path = uri;
rr.content_type = content_type;
rr.rc = response;
responses.emplace_back(std::move(rr));
}
void append_redirect(const std::string &uri, const std::string &target)
{
redirect_map[uri] = target;
}
void reset_redirect()
{
std::map<std::string, std::string>().swap(redirect_map);
}
int start_web_server(listener_args *args);
int start_web_server_multi(listener_args *args);
std::vector<responseRoute> responses;
string_map redirect_map;
};
+6 -31
View File
@@ -2,6 +2,7 @@
#ifdef MALLOC_TRIM
#include <malloc.h>
#endif // MALLOC_TRIM
#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 16384
#include "httplib.h"
#include "../utils/base64/base64.h"
@@ -11,11 +12,6 @@
#include "../utils/urlencode.h"
#include "webserver.h"
int WebServer::start_web_server(void *argv)
{
return start_web_server_multi(argv);
}
void WebServer::stop_web_server()
{
SERVER_EXIT_FLAG = true;
@@ -33,11 +29,7 @@ static httplib::Server::Handler makeHandler(const responseRoute &rr)
{
req.headers[h.first] = h.second;
}
for (auto &p: request.params)
{
req.argument += p.first + "=" + p.second + "&";
}
req.argument.pop_back();
req.argument = request.params;
req.postdata = request.body;
if (request.get_header_value("Content-Type") == "application/x-www-form-urlencoded")
{
@@ -57,10 +49,9 @@ static httplib::Server::Handler makeHandler(const responseRoute &rr)
};
}
int WebServer::start_web_server_multi(void *argv)
int WebServer::start_web_server_multi(listener_args *args)
{
httplib::Server server;
auto *args = (listener_args *)argv;
for (auto &x : responses)
{
switch (hash_(x.method))
@@ -104,7 +95,7 @@ int WebServer::start_web_server_multi(void *argv)
std::string params;
for (auto &p: req.params)
{
params += p.first + "=" + p.second + "&";
params += p.first + "=" + urlEncode(p.second) + "&";
}
if (!params.empty())
{
@@ -201,23 +192,7 @@ int WebServer::start_web_server_multi(void *argv)
return 0;
}
void WebServer::append_response(const std::string &method, const std::string &uri, const std::string &content_type, response_callback response)
int WebServer::start_web_server(listener_args *args)
{
responseRoute rr;
rr.method = method;
rr.path = uri;
rr.content_type = content_type;
rr.rc = response;
responses.emplace_back(std::move(rr));
return start_web_server_multi(args);
}
void WebServer::append_redirect(const std::string &uri, const std::string &target)
{
redirect_map[uri] = target;
}
void WebServer::reset_redirect()
{
eraseElements(redirect_map);
}
+19 -35
View File
@@ -112,14 +112,22 @@ static inline void buffer_cleanup(struct evbuffer *eb)
#endif // MALLOC_TRIM
}
inline int process_request(WebServer *server, Request &request, Response &response, std::string &return_data)
static int process_request(WebServer *server, Request &request, Response &response, std::string &return_data)
{
writeLog(0, "handle_cmd: " + request.method + " handle_uri: " + request.url, LOG_LEVEL_VERBOSE);
string_size pos = request.url.find('?');
if(pos != std::string::npos)
{
request.argument = request.url.substr(pos + 1);
auto argument = split(request.url.substr(pos + 1), "&");
for(auto &x : argument)
{
string_size pos2 = x.find('=');
if(pos2 != std::string::npos)
request.argument.emplace(x.substr(0, pos2), x.substr(pos2 + 1));
else
request.argument.emplace(x, "");
}
request.url.erase(pos);
}
@@ -143,7 +151,7 @@ inline int process_request(WebServer *server, Request &request, Response &respon
}
catch(std::exception &e)
{
return_data = "Internal server error while processing request path '" + request.url + "' with arguments '" + request.argument + "'!";
return_data = "Internal server error while processing request path '" + request.url + "' with arguments '" + joinArguments(request.argument) + "'!";
return_data += "\n exception: ";
return_data += type(e);
return_data += "\n what(): ";
@@ -163,9 +171,9 @@ inline int process_request(WebServer *server, Request &request, Response &respon
if(!request.argument.empty())
{
if(return_data.find('?') != std::string::npos)
return_data += "&" + request.argument;
return_data += "&" + joinArguments(request.argument);
else
return_data += "?" + request.argument;
return_data += "?" + joinArguments(request.argument);
}
return 2;
}
@@ -179,7 +187,7 @@ inline int process_request(WebServer *server, Request &request, Response &respon
return -1;
}
void on_request(evhttp_request *req, void *args)
static void on_request(evhttp_request *req, void *args)
{
auto server = (WebServer*) args;
static std::string auth_token = "Basic " + base64Encode(server->auth_user + ":" + server->auth_password);
@@ -293,9 +301,8 @@ void on_request(evhttp_request *req, void *args)
buffer_cleanup(output_buffer);
}
int WebServer::start_web_server(void *argv)
int WebServer::start_web_server(listener_args *args)
{
auto *args = reinterpret_cast<listener_args*>(argv);
std::string listen_address = args->listen_address;
int port = args->port;
if (!event_init())
@@ -314,10 +321,8 @@ int WebServer::start_web_server(void *argv)
return -1;
}
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_REQ_HEAD);
evhttp_set_gencb(server.get(), wrap(call_on_request), this);
evhttp_set_gencb(server.get(), on_request, this);
evhttp_set_timeout(server.get(), 30);
if (event_dispatch() == -1)
{
@@ -329,14 +334,14 @@ int WebServer::start_web_server(void *argv)
return 0;
}
void* httpserver_dispatch(void *arg)
static void* httpserver_dispatch(void *arg)
{
event_base_dispatch(reinterpret_cast<event_base*>(arg));
event_base_free(reinterpret_cast<event_base*>(arg)); //free resources
return nullptr;
}
int httpserver_bindsocket(std::string listen_address, int listen_port, int backlog)
static int httpserver_bindsocket(std::string listen_address, int listen_port, int backlog)
{
SOCKET nfd;
nfd = socket(AF_INET, SOCK_STREAM, 0);
@@ -375,9 +380,8 @@ int httpserver_bindsocket(std::string listen_address, int listen_port, int backl
return nfd;
}
int WebServer::start_web_server_multi(void *argv)
int WebServer::start_web_server_multi(listener_args *args)
{
auto *args = reinterpret_cast<listener_args*>(argv);
std::string listen_address = args->listen_address;
int port = args->port, nthreads = args->max_workers, max_conn = args->max_conn;
@@ -426,23 +430,3 @@ void WebServer::stop_web_server()
{
SERVER_EXIT_FLAG = true;
}
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;
rr.path = uri;
rr.content_type = content_type;
rr.rc = response;
responses.emplace_back(std::move(rr));
}
void WebServer::append_redirect(const std::string &uri, const std::string &target)
{
redirect_map[uri] = target;
}
void WebServer::reset_redirect()
{
eraseElements(redirect_map);
}
+4 -5
View File
@@ -21,13 +21,12 @@ enum
INIREADER_EXCEPTION_NONE
};
using ini_data_struct = std::map<std::string, std::multimap<std::string, std::string>>;
using string_multimap = std::multimap<std::string, std::string>;
using string_array = std::vector<std::string>;
using string_size = std::string::size_type;
class INIReader
{
using ini_data_struct = std::map<std::string, std::multimap<std::string, std::string>>;
using string_multimap = std::multimap<std::string, std::string>;
using string_array = std::vector<std::string>;
using string_size = std::string::size_type;
/**
* @brief A simple INI reader which utilize map and vector
* to store sections and items, allowing access in logarithmic time.
+9
View File
@@ -7,6 +7,7 @@
#include <time.h>
#include "string.h"
#include "map_extra.h"
std::vector<std::string> split(const std::string &s, const std::string &seperator)
{
@@ -298,6 +299,14 @@ std::string getUrlArg(const std::string &url, const std::string &request)
return "";
}
std::string getUrlArg(const string_multimap &url, const std::string &request)
{
auto it = url.find(request);
if(it != url.end())
return it->second;
return "";
}
std::string replaceAllDistinct(std::string str, const std::string &old_value, const std::string &new_value)
{
for(std::string::size_type pos(0); pos != std::string::npos; pos += new_value.length())
+2
View File
@@ -11,6 +11,7 @@ using string = std::string;
using string_size = std::string::size_type;
using string_array = std::vector<std::string>;
using string_map = std::map<std::string, std::string>;
using string_multimap = std::multimap<std::string, std::string>;
using string_pair_array = std::vector<std::pair<std::string, std::string>>;
std::vector<std::string> split(const std::string &s, const std::string &seperator);
@@ -27,6 +28,7 @@ std::string join(InputIt first, InputIt last, const std::string &delimiter)
}
std::string getUrlArg(const std::string &url, const std::string &request);
std::string getUrlArg(const string_multimap &url, const std::string &request);
std::string replaceAllDistinct(std::string str, const std::string &old_value, const std::string &new_value);
std::string trimOf(const std::string& str, char target, bool before = true, bool after = true);
std::string trim(const std::string& str, bool before = true, bool after = true);
+14
View File
@@ -69,3 +69,17 @@ std::string urlDecode(const std::string& str)
}
return strTemp;
}
std::string joinArguments(const string_multimap &args)
{
std::string strTemp;
for (auto &p: args)
{
strTemp += p.first + "=" + urlEncode(p.second) + "&";
}
if (!strTemp.empty())
{
strTemp.pop_back();
}
return strTemp;
}
+3
View File
@@ -3,7 +3,10 @@
#include <string>
#include "utils/string.h"
std::string urlEncode(const std::string& str);
std::string urlDecode(const std::string& str);
std::string joinArguments(const string_multimap &args);
#endif // URLENCODE_H_INCLUDED