Просмотр исходного кода

Enhancements

Add Edge header support for Surge/Quantumult/Clash.
Fix configuration file not found when start up via right-click menu on some platform.
Optimize codes.
Clean up unused files.
Tindy X 6 лет назад
Родитель
Сommit
e2aacf853c

BIN
base/rules/lhie1/Surge/Surge 3/Provider/Media/.DS_Store


+ 0 - 7
base/rules/lhie1/Surge/Surge 3/Provider/Media/AbemaTV.list

@@ -1,7 +0,0 @@
-# > AbemaTV
-USER-AGENT,AbemaTV*
-DOMAIN-SUFFIX,abema.io
-DOMAIN-SUFFIX,abema.tv
-DOMAIN-SUFFIX,akamaized.net
-DOMAIN-SUFFIX,ameba.jp
-DOMAIN-SUFFIX,hayabusa.io

+ 0 - 12
base/rules/lhie1/Surge/Surge 3/Provider/Media/AppleNews.list

@@ -1,12 +0,0 @@
-# > Apple News and Apple Map TOMTOM Version
-DOMAIN,gspe1-ssl.ls.apple.com
-PROCESS-NAME,News
-USER-AGENT,AppleNews*
-USER-AGENT,com.apple.news*
-#USER-AGENT,News*
-#DOMAIN,gateway.icloud.com
-#DOMAIN,news-events.apple.com
-#DOMAIN,apple.comscoreresearch.com
-#DOMAIN-SUFFIX,apple.news
-#DOMAIN,news-client.apple.com
-#DOMAIN,news-edge.apple.com

+ 0 - 6
base/rules/lhie1/Surge/Surge 3/Provider/Media/BBC_iPlayer.list

@@ -1,6 +0,0 @@
-# > BBC iPlayer
-USER-AGENT,BBCiPlayer*
-DOMAIN-SUFFIX,bbc.co.uk
-DOMAIN-SUFFIX,bbci.co.uk
-DOMAIN-KEYWORD,bbcfmt
-DOMAIN-KEYWORD,uk-live

+ 0 - 5
base/rules/lhie1/Surge/Surge 3/Provider/Media/Fox_Now.list

@@ -1,5 +0,0 @@
-# > Fox Now
-USER-AGENT,FOX%20NOW*
-DOMAIN-SUFFIX,fox.com
-DOMAIN-SUFFIX,foxdcg.com
-DOMAIN-SUFFIX,uplynk.com

+ 0 - 3
base/rules/lhie1/Surge/Surge 3/Provider/Media/Hulu_Japan.list

@@ -1,3 +0,0 @@
-# > Hulu(フールー)
-DOMAIN-SUFFIX,happyon.jp
-DOMAIN-SUFFIX,hulu.jp

+ 0 - 4
base/rules/lhie1/Surge/Surge 3/Provider/Media/Line_TV.list

@@ -1,4 +0,0 @@
-# > Line TV
-USER-AGENT,LINE*
-DOMAIN-SUFFIX,d3c7rimkq79yfu.cloudfront.net
-DOMAIN-SUFFIX,linetv.tw

+ 0 - 5
base/rules/lhie1/Surge/Surge 3/Provider/Media/Netease_Music.list

@@ -1,5 +0,0 @@
-# > NeteaseMusic
-USER-AGENT,%E7%BD%91%E6%98%93%E4%BA%91%E9%9F%B3%E4%B9%90
-USER-AGENT,NeteaseMusic*
-DOMAIN-SUFFIX,music.126.net
-DOMAIN-SUFFIX,music.163.com

+ 0 - 3
base/rules/lhie1/Surge/Surge 3/Provider/Media/YouTube_Music.list

@@ -1,3 +0,0 @@
-# > Youtube_Music
-USER-AGENT,*YouTubeMusic*
-USER-AGENT,*com.google.ios.youtubemusic*

+ 0 - 4
base/rules/lhie1/Surge/Surge 3/Provider/Media/myTV_SUPER.list

@@ -1,4 +0,0 @@
-# > myTV_SUPER
-USER-AGENT,mytv*
-DOMAIN-SUFFIX,mytvsuper.com
-DOMAIN-SUFFIX,tvb.com

+ 24 - 1
src/main.cpp

@@ -61,6 +61,21 @@ void chkArg(int argc, char *argv[])
     }
 }
 
+void signal_handler(int sig)
+{
+    std::cerr<<"Interrupt signal "<<sig<<" received. Exiting gracefully...\n";
+    switch(sig)
+    {
+#ifndef _WIN32
+    case SIGHUP:
+    case SIGQUIT:
+#endif // _WIN32
+    case SIGTERM:
+    case SIGINT:
+        stop_web_server();
+        break;
+    }
+}
 int main(int argc, char *argv[])
 {
 #ifdef _WIN32
@@ -74,13 +89,21 @@ int main(int argc, char *argv[])
 #else
     signal(SIGPIPE, SIG_IGN);
     signal(SIGABRT, SIG_IGN);
+    signal(SIGHUP, signal_handler);
+    signal(SIGQUIT, signal_handler);
 #endif // _WIN32
+    signal(SIGTERM, signal_handler);
+    signal(SIGINT, signal_handler);
 
     SetConsoleTitle("subconverter " VERSION);
+#ifndef _DEBUG
+    std::string prgpath = argv[0];
+    setcd(prgpath); //first switch to program directory
+#endif // _DEBUG
     if(fileExist("pref.yml"))
         pref_path = "pref.yml";
     chkArg(argc, argv);
-    setcd(pref_path);
+    setcd(pref_path); //then switch to pref directory
     readConf();
     if(!update_ruleset_on_request)
         refreshRulesets(rulesets, ruleset_content_array);

+ 21 - 13
src/speedtestutil.cpp

@@ -94,7 +94,7 @@ void explodeVmess(std::string vmess, std::string custom_port, int local_port, no
     node.remarks = ps;
     node.server = add;
     node.port = to_int(port, 0);
-    node.proxyStr = vmessConstruct(add, port, type, id, aid, net, "auto", path, host, tls, local_port);
+    node.proxyStr = vmessConstruct(add, port, type, id, aid, net, "auto", path, host, "", tls, local_port);
 }
 
 void explodeVmessConf(std::string content, std::string custom_port, int local_port, bool libev, std::vector<nodeInfo> &nodes)
@@ -181,7 +181,7 @@ void explodeVmessConf(std::string content, std::string custom_port, int local_po
             json["vmess"][i]["security"] >> cipher;
             group = V2RAY_DEFAULT_GROUP;
             node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
-            node.proxyStr = vmessConstruct(add, port, type, id, aid, net, cipher, path, host, tls, local_port);
+            node.proxyStr = vmessConstruct(add, port, type, id, aid, net, cipher, path, host, "", tls, local_port);
             break;
         case 3: //ss config
             json["vmess"][i]["id"] >> id;
@@ -579,7 +579,7 @@ void explodeSocks(std::string link, std::string custom_port, nodeInfo &node)
 void explodeQuan(std::string quan, std::string custom_port, int local_port, nodeInfo &node)
 {
     std::string strTemp, itemName, itemVal;
-    std::string group = V2RAY_DEFAULT_GROUP, ps, add, port, cipher = "auto", type = "none", id, aid = "0", net = "tcp", path, host, tls;
+    std::string group = V2RAY_DEFAULT_GROUP, ps, add, port, cipher = "auto", type = "none", id, aid = "0", net = "tcp", path, host, edge, tls;
     std::vector<std::string> configs, vArray;
     strTemp = regReplace(quan, "(.*?) = (.*)", "$1,$2");
     configs = split(strTemp, ",");
@@ -614,6 +614,8 @@ void explodeQuan(std::string quan, std::string custom_port, int local_port, node
                 {
                     if(strFind(headers[j], "Host: "))
                         host = headers[j].substr(6);
+                    else if(strFind(headers[j], "Edge: "))
+                        edge = headers[j].substr(6);
                 }
             }
             else if(itemName == "obfs" && itemVal == "ws")
@@ -627,14 +629,14 @@ void explodeQuan(std::string quan, std::string custom_port, int local_port, node
         node.remarks = ps;
         node.server = add;
         node.port = to_int(port, 0);
-        node.proxyStr = vmessConstruct(add, port, type, id, aid, net, cipher, path, host, tls, local_port);
+        node.proxyStr = vmessConstruct(add, port, type, id, aid, net, cipher, path, host, edge, tls, local_port);
     }
 }
 
 void explodeNetch(std::string netch, bool ss_libev, bool ssr_libev, std::string custom_port, int local_port, nodeInfo &node)
 {
     Document json;
-    std::string type, remark, address, port, username, password, method, plugin, pluginopts, protocol, protoparam, obfs, obfsparam, id, aid, transprot, faketype, host, path, tls;
+    std::string type, remark, address, port, username, password, method, plugin, pluginopts, protocol, protoparam, obfs, obfsparam, id, aid, transprot, faketype, host, edge, path, tls;
     netch = urlsafe_base64_decode(netch.substr(8));
 
     json.Parse(netch.data());
@@ -685,10 +687,11 @@ void explodeNetch(std::string netch, bool ss_libev, bool ssr_libev, std::string
         faketype = GetMember(json, "FakeType");
         host = GetMember(json, "Host");
         path = GetMember(json, "Path");
+        edge = GetMember(json, "Edge");
         tls = GetMember(json, "TLSSecure") == "true" ? "tls" : "";
         node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
         node.group = V2RAY_DEFAULT_GROUP;
-        node.proxyStr = vmessConstruct(address, port, faketype, id, aid, transprot, method, path, host, tls, local_port);
+        node.proxyStr = vmessConstruct(address, port, faketype, id, aid, transprot, method, path, host, edge, tls, local_port);
     }
     else if(type == "Socks5")
     {
@@ -711,7 +714,7 @@ void explodeClash(Node yamlnode, std::string custom_port, int local_port, std::v
     Node singleproxy;
     unsigned int index = nodes.size();
     std::string proxytype, ps, server, port, cipher, group, password; //common
-    std::string type = "none", id, aid = "0", net = "tcp", path, host, tls; //vmess
+    std::string type = "none", id, aid = "0", net = "tcp", path, host, edge, tls; //vmess
     std::string plugin, pluginopts, pluginopts_mode, pluginopts_host, pluginopts_mux; //ss
     std::string protocol, protoparam, obfs, obfsparam; //ssr
     std::string user; //socks
@@ -736,13 +739,16 @@ void explodeClash(Node yamlnode, std::string custom_port, int local_port, std::v
             else
                 tls.clear();
             if(singleproxy["ws-headers"].IsDefined())
+            {
                 singleproxy["ws-headers"]["Host"] >> host;
+                singleproxy["ws-headers"]["Edge"] >> edge;
+            }
             else
                 host.clear();
 
 
             node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
-            node.proxyStr = vmessConstruct(server, port, type, id, aid, net, cipher, path, host, tls, local_port);
+            node.proxyStr = vmessConstruct(server, port, type, id, aid, net, cipher, path, host, edge, tls, local_port);
         }
         else if(proxytype == "ss")
         {
@@ -940,7 +946,7 @@ void explodeShadowrocket(std::string rocket, std::string custom_port, int local_
     node.remarks = remarks;
     node.server = add;
     node.port = to_int(port, 0);
-    node.proxyStr = vmessConstruct(add, port, type, id, aid, net, cipher, path, host, tls, local_port);
+    node.proxyStr = vmessConstruct(add, port, type, id, aid, net, cipher, path, host, "", tls, local_port);
 }
 
 void explodeKitsunebi(std::string kit, std::string custom_port, int local_port, nodeInfo &node)
@@ -985,14 +991,14 @@ void explodeKitsunebi(std::string kit, std::string custom_port, int local_port,
     node.remarks = remarks;
     node.server = add;
     node.port = to_int(port, 0);
-    node.proxyStr = vmessConstruct(add, port, type, id, aid, net, cipher, path, host, tls, local_port);
+    node.proxyStr = vmessConstruct(add, port, type, id, aid, net, cipher, path, host, "", tls, local_port);
 }
 
 bool explodeSurge(std::string surge, std::string custom_port, int local_port, std::vector<nodeInfo> &nodes, bool libev)
 {
     std::string remarks, server, port, method, username, password; //common
     std::string plugin, pluginopts, pluginopts_mode, pluginopts_host = "cloudfront.net", mod_url, mod_md5; //ss
-    std::string id, net, tls, host, path; //v2
+    std::string id, net, tls, host, edge, path; //v2
     std::string protocol, protoparam; //ssr
     std::string itemName, itemVal;
     std::vector<std::string> configs, vArray, headers, header;
@@ -1157,6 +1163,8 @@ bool explodeSurge(std::string surge, std::string custom_port, int local_port, st
                         header = split(trim(y), ":");
                         if(header[0] == "Host")
                             host = header[1];
+                        else if(header[0] == "Edge")
+                            edge = header[1];
                     }
                 }
             }
@@ -1165,7 +1173,7 @@ bool explodeSurge(std::string surge, std::string custom_port, int local_port, st
 
             node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
             node.group = V2RAY_DEFAULT_GROUP;
-            node.proxyStr = vmessConstruct(server, port, "", id, "0", net, method, path, host, tls, local_port);
+            node.proxyStr = vmessConstruct(server, port, "", id, "0", net, method, path, host, edge, tls, local_port);
         }
         else if(configs[0] == "http") //http proxy
         {
@@ -1274,7 +1282,7 @@ bool explodeSurge(std::string surge, std::string custom_port, int local_port, st
 
             node.linkType = SPEEDTEST_MESSAGE_FOUNDVMESS;
             node.group = V2RAY_DEFAULT_GROUP;
-            node.proxyStr = vmessConstruct(server, port, "", id, "0", net, method, path, host, tls, local_port);
+            node.proxyStr = vmessConstruct(server, port, "", id, "0", net, method, path, host, "", tls, local_port);
         }
         else
             continue;

+ 1 - 1
src/speedtestutil.h

@@ -7,7 +7,7 @@
 #include "misc.h"
 #include "nodeinfo.h"
 
-std::string vmessConstruct(std::string add, std::string port, std::string type, std::string id, std::string aid, std::string net, std::string cipher, std::string path, std::string host, std::string tls, int local_port);
+std::string vmessConstruct(std::string add, std::string port, std::string type, std::string id, std::string aid, std::string net, std::string cipher, std::string path, std::string host, std::string edge, std::string tls, int local_port);
 std::string ssrConstruct(std::string group, std::string remarks, std::string remarks_base64, std::string server, std::string port, std::string protocol, std::string method, std::string obfs, std::string password, std::string obfsparam, std::string protoparam, int local_port, bool libev);
 std::string ssConstruct(std::string server, std::string port, std::string password, std::string method, std::string plugin, std::string pluginopts, std::string remarks, int local_port, bool libev);
 std::string socksConstruct(std::string remarks, std::string server, std::string port, std::string username, std::string password);

+ 24 - 6
src/subexport.cpp

@@ -58,7 +58,7 @@ std::string hostnameToIPAddr(std::string host)
 }
 */
 
-std::string vmessConstruct(std::string add, std::string port, std::string type, std::string id, std::string aid, std::string net, std::string cipher, std::string path, std::string host, std::string tls, int local_port)
+std::string vmessConstruct(std::string add, std::string port, std::string type, std::string id, std::string aid, std::string net, std::string cipher, std::string path, std::string host, std::string edge, std::string tls, int local_port)
 {
     if(!path.size())
         path = "/";
@@ -90,6 +90,8 @@ std::string vmessConstruct(std::string add, std::string port, std::string type,
     writer.String(net.data());
     writer.Key("Host");
     writer.String(host.data());
+    writer.Key("Edge");
+    writer.String(edge.data());
     if(net == "ws")
     {
         writer.Key("Path");
@@ -557,7 +559,7 @@ void netchToClash(std::vector<nodeInfo> &nodes, YAML::Node &yamlnode, string_arr
     std::string type, remark, hostname, port, username, password, method;
     std::string plugin, pluginopts;
     std::string protocol, protoparam, obfs, obfsparam;
-    std::string id, aid, transproto, faketype, host, path, quicsecure, quicsecret;
+    std::string id, aid, transproto, faketype, host, edge, path, quicsecure, quicsecret;
     std::vector<nodeInfo> nodelist;
     bool tlssecure, replace_flag;
     string_array vArray, remarks_list, filtered_nodelist;
@@ -637,6 +639,7 @@ void netchToClash(std::vector<nodeInfo> &nodes, YAML::Node &yamlnode, string_arr
             aid = GetMember(json, "AlterID");
             transproto = GetMember(json, "TransferProtocol");
             host = GetMember(json, "Host");
+            edge = GetMember(json, "Edge");
             path = GetMember(json, "Path");
             tlssecure = GetMember(json, "TLSSecure") == "true";
             singleproxy["type"] = "vmess";
@@ -651,6 +654,12 @@ void netchToClash(std::vector<nodeInfo> &nodes, YAML::Node &yamlnode, string_arr
                 singleproxy["network"] = transproto;
                 singleproxy["ws-path"] = path;
                 singleproxy["ws-headers"]["Host"] = host;
+                singleproxy["headers"]["Host"] = host;
+                if(edge.size())
+                {
+                    singleproxy["ws-headers"]["Edge"] = edge;
+                    singleproxy["headers"]["Edge"] = edge;
+                }
             }
             else if(transproto == "kcp" || transproto == "h2" || transproto == "quic")
                 continue;
@@ -662,7 +671,7 @@ void netchToClash(std::vector<nodeInfo> &nodes, YAML::Node &yamlnode, string_arr
             obfs = GetMember(json, "OBFS");
             if(ext.filter_deprecated)
             {
-                if(method == "chacha20")
+                if(method == "chacha20" && !clashR) //the mainline core no longer supports chacha20, but clashR core still does
                     continue;
                 if(std::find(clashr_protocols.cbegin(), clashr_protocols.cend(), protocol) == clashr_protocols.cend())
                     continue;
@@ -805,7 +814,7 @@ std::string netchToSurge(std::vector<nodeInfo> &nodes, std::string &base_conf, s
     std::string type, remark, hostname, port, username, password, method;
     std::string plugin, pluginopts;
     std::string protocol, protoparam, obfs, obfsparam;
-    std::string id, aid, transproto, faketype, host, path, quicsecure, quicsecret;
+    std::string id, aid, transproto, faketype, host, edge, path, quicsecure, quicsecret;
     std::string output_nodelist;
     std::vector<nodeInfo> nodelist;
     unsigned short local_port = 1080;
@@ -886,12 +895,15 @@ std::string netchToSurge(std::vector<nodeInfo> &nodes, std::string &base_conf, s
             aid = GetMember(json, "AlterID");
             transproto = GetMember(json, "TransferProtocol");
             host = GetMember(json, "Host");
+            edge = GetMember(json, "Edge");
             path = GetMember(json, "Path");
             tlssecure = GetMember(json, "TLSSecure") == "true";
             proxy = "vmess, " + hostname + ", " + port + ", username=" + id + ", tls=" + (tlssecure ? "true" : "false");
             if(transproto == "ws")
             {
                 proxy += ", ws=true, ws-path=" + path + ", ws-headers=Host:" + host;
+                if(edge.size())
+                    proxy += "|Edge:" + edge;
             }
             if(ext.skip_cert_verify)
                 proxy += ", skip-cert-verify=1";
@@ -1317,7 +1329,7 @@ void netchToQuan(std::vector<nodeInfo> &nodes, INIReader &ini, std::vector<rules
     std::string remark, hostname, port, method, password;
     std::string plugin, pluginopts;
     std::string protocol, protoparam, obfs, obfsparam;
-    std::string id, aid, transproto, faketype, host, path, quicsecure, quicsecret;
+    std::string id, aid, transproto, faketype, host, edge, path, quicsecure, quicsecret;
     std::string proxyStr, allLinks;
     bool tlssecure;
     std::vector<nodeInfo> nodelist;
@@ -1369,6 +1381,7 @@ void netchToQuan(std::vector<nodeInfo> &nodes, INIReader &ini, std::vector<rules
             transproto = GetMember(json, "TransferProtocol");
             host = GetMember(json, "Host");
             path = GetMember(json, "Path");
+            edge = GetMember(json, "Edge");
             faketype = GetMember(json, "FakeType");
             tlssecure = GetMember(json, "TLSSecure") == "true";
 
@@ -1378,7 +1391,12 @@ void netchToQuan(std::vector<nodeInfo> &nodes, INIReader &ini, std::vector<rules
             if(tlssecure)
                 proxyStr += ", over-tls=true, tls-host=" + host;
             if(transproto == "ws")
-                proxyStr += ", obfs=ws, obfs-path=\"" + path + "\", obfs-header=\"Host: " + host + "\"";
+            {
+                proxyStr += ", obfs=ws, obfs-path=\"" + path + "\", obfs-header=\"Host: " + host;
+                if(edge.size())
+                    proxyStr += "[Rr][Nn]Edge: " + edge;
+                proxyStr += "\"";
+            }
 
             if(ext.nodelist)
                 proxyStr = "vmess://" + urlsafe_base64_encode(proxyStr);

+ 21 - 44
src/webserver.cpp

@@ -1,7 +1,7 @@
 #include <iostream>
 #include <fstream>
 #include <map>
-#include <mutex>
+#include <atomic>
 #include <thread>
 #include <pthread.h>
 
@@ -23,44 +23,9 @@ struct responseRoute
 std::vector<responseRoute> responses;
 
 //for use of multi-thread
-typedef std::lock_guard<std::mutex> guarded_mutex;
-int working_thread = 0, max_send_failure = 10;
-bool SERVER_EXIT_FLAG = false;
-std::mutex working_mutex, server_exit_flag_mutex;
-
-static inline void working_acc()
-{
-    guarded_mutex guard(working_mutex);
-    working_thread++;
-}
-
-static inline void working_dec()
-{
-    guarded_mutex guard(working_mutex);
-    working_thread--;
-}
-
-static inline int safe_read_working()
-{
-    int retVal;
-    guarded_mutex guard(working_mutex);
-    retVal = working_thread;
-    return retVal;
-}
-
-static inline bool safe_read_server_exit_flag()
-{
-    bool retVal;
-    guarded_mutex guard(server_exit_flag_mutex);
-    retVal = SERVER_EXIT_FLAG;
-    return retVal;
-}
-
-static inline void safe_set_server_exit_flag()
-{
-    guarded_mutex guard(server_exit_flag_mutex);
-    SERVER_EXIT_FLAG = true;
-}
+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)
 {
@@ -214,7 +179,7 @@ int setTimeout(SOCKET s, int timeout)
     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 = {0, timeout * 1000};
+    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
@@ -224,7 +189,7 @@ int setTimeout(SOCKET s, int timeout)
 
 void handle_req(std::string request, int client_sock)
 {
-    working_acc();
+    working_thread++;
     std::cerr<<"worker startup"<<std::endl;
     string_array vArray;
     char command[16] = {};
@@ -279,7 +244,7 @@ end:
     std::cerr<<"worker stop"<<std::endl;
     sleep(1);
     closesocket(client_sock);
-    working_dec();
+    working_thread--;
 }
 
 void append_response(std::string type, std::string request, std::string content_type, response_callback response)
@@ -292,6 +257,18 @@ void append_response(std::string type, std::string request, std::string content_
     responses.push_back(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
@@ -373,10 +350,10 @@ int start_web_server_multi(void *argv)
 
         //handle_req(buf, acc_socket);
 
-        while(safe_read_working() >= max_workers)
+        while(working_thread >= max_workers)
         {
             sleep(10);
-            if(safe_read_server_exit_flag())
+            if(SERVER_EXIT_FLAG)
                 break;
         }
         while(workers[worker_index].get_id() != std::thread::id())

+ 1 - 0
src/webserver.h

@@ -16,5 +16,6 @@ struct listener_args
 void append_response(std::string method, std::string uri, std::string content_type, response_callback response);
 int start_web_server(void *argv);
 int start_web_server_multi(void *argv);
+void stop_web_server();
 
 #endif // WEBSERVER_H_INCLUDED

+ 18 - 6
src/webserver_libevent.cpp

@@ -2,6 +2,7 @@
 #include <cstdint>
 #include <iostream>
 #include <evhttp.h>
+#include <atomic>
 #ifdef MALLOC_TRIM
 #include <malloc.h>
 #endif // MALLOC_TRIM
@@ -17,6 +18,7 @@
 #include "socket.h"
 
 extern std::string user_agent_str;
+std::atomic_bool SERVER_EXIT_FLAG(false);
 
 struct responseRoute
 {
@@ -178,6 +180,7 @@ int start_web_server(void *argv)
 void* httpserver_dispatch(void *arg)
 {
     event_base_dispatch((struct event_base*)arg);
+    event_base_free((struct event_base*)arg);
     return NULL;
 }
 
@@ -235,12 +238,13 @@ int start_web_server_multi(void *argv)
         return -1;
 
     pthread_t ths[nthreads];
+    struct event_base *base[nthreads];
     for (i = 0; i < nthreads; i++)
     {
-        struct event_base *base = event_init();
-        if (base == NULL)
+        base[i] = event_init();
+        if (base[i] == NULL)
             return -1;
-        struct evhttp *httpd = evhttp_new(base);
+        struct evhttp *httpd = evhttp_new(base[i]);
         if (httpd == NULL)
             return -1;
         ret = evhttp_accept_socket(httpd, nfd);
@@ -249,16 +253,24 @@ int start_web_server_multi(void *argv)
 
         evhttp_set_allowed_methods(httpd, EVHTTP_REQ_GET | EVHTTP_REQ_POST | EVHTTP_REQ_OPTIONS);
         evhttp_set_gencb(httpd, OnReq, nullptr);
-        ret = pthread_create(&ths[i], NULL, httpserver_dispatch, base);
+        ret = pthread_create(&ths[i], NULL, httpserver_dispatch, base[i]);
         if (ret != 0)
             return -1;
     }
-    while(true)
-        sleep(10000); //block forever
+    while(!SERVER_EXIT_FLAG)
+        sleep(200); //block forever until receive stop signal
+
+    for (i = 0; i < nthreads; i++)
+        event_base_loopbreak(base[i]);
 
     return 0;
 }
 
+void stop_web_server()
+{
+    SERVER_EXIT_FLAG = true;
+}
+
 void append_response(std::string method, std::string uri, std::string content_type, response_callback response)
 {
     responseRoute rr;