diff --git a/openwrt/files/luci/statistics.htm b/openwrt/files/luci/statistics.htm
index b117913..c4c840c 100644
--- a/openwrt/files/luci/statistics.htm
+++ b/openwrt/files/luci/statistics.htm
@@ -1,14 +1,26 @@
<%
-local stats = {}
-local file = io.open("/var/log/ua3f/stats", "r")
-if file then
- for line in file:lines() do
+local rewrite_stats = {}
+local rewrite_stats_file = io.open("/var/log/ua3f/rewrite_stats", "r")
+if rewrite_stats_file then
+ for line in rewrite_stats_file:lines() do
local host, count, origin_ua, mocked_ua = line:match("^(%S+)%s+(%d+)%s+(.-)SEQSEQ(.-)%s*$")
if host and count then
- table.insert(stats, {host = host, count = count, origin_ua = origin_ua, mocked_ua = mocked_ua})
+ table.insert(rewrite_stats, {host = host, count = count, origin_ua = origin_ua, mocked_ua = mocked_ua})
end
end
- file:close()
+ rewrite_stats_file:close()
+end
+
+local pass_stats = {}
+local pass_stats_file = io.open("/var/log/ua3f/passthrough_stats", "r")
+if pass_stats_file then
+ for line in pass_stats_file:lines() do
+ local host, count, ua = line:match("^(%S+)%s(%d+)%s(.+)$")
+ if ua and count then
+ table.insert(pass_stats, {ua = ua, count = count, host = host})
+ end
+ end
+ pass_stats_file:close()
end
local function rowstyle(i)
@@ -17,9 +29,9 @@ end
%>
<%:Statistics%>
-<%:User-Agent Rewrite Statistics%>
-
+<%:User-Agent Rewrite Statistics%>
+
| <%:Host%> |
<%:Rewrite Count%> |
@@ -27,7 +39,7 @@ end
<%:Modified User-Agent%> |
- <% for i, item in ipairs(stats) do %>
+ <% for i, item in ipairs(rewrite_stats) do %>
| <%= item.host %> |
<%= item.count %> |
@@ -37,6 +49,24 @@ end
<% end %>
+<%:User-Agent Pass-Through Statistics%>
+
+
+ | <%:User-Agent%> |
+ <%:Pass-Through Count%> |
+ <%:Last Host%> |
+
+
+ <% for i, item in ipairs(pass_stats) do %>
+
+ | <%= item.ua %> |
+ <%= item.count %> |
+ <%= item.host %> |
+
+ <% end %>
+
+
+
diff --git a/openwrt/po/zh_cn/ua3f.po b/openwrt/po/zh_cn/ua3f.po
index 33896ce..c66ad80 100644
--- a/openwrt/po/zh_cn/ua3f.po
+++ b/openwrt/po/zh_cn/ua3f.po
@@ -63,4 +63,13 @@ msgid "Original User-Agent"
msgstr "原始 User-Agent"
msgid "Modified User-Agent"
-msgstr "重写后 User-Agent"
\ No newline at end of file
+msgstr "重写后 User-Agent"
+
+msgid "User-Agent Pass-Through Statistics"
+msgstr "User-Agent 放行次数实时统计"
+
+msgid "Pass-Through Count"
+msgstr "放行次数"
+
+msgid "Last Host"
+msgstr "最后访问地址"
diff --git a/src/internal/rewrite/rewriter.go b/src/internal/rewrite/rewriter.go
index 7ddcdcc..ffe2d14 100644
--- a/src/internal/rewrite/rewriter.go
+++ b/src/internal/rewrite/rewriter.go
@@ -148,6 +148,10 @@ func (r *Rewriter) ProxyHTTPOrRaw(dst net.Conn, src net.Conn, destAddrPort strin
destAddrPort, srcAddr, originalUA, r.cache.Len())
r.cache.Add(destAddrPort, destAddrPort)
}
+ statistics.AddPassThroughRecord(&statistics.PassThroughRecord{
+ Host: destAddrPort,
+ UA: originalUA,
+ })
if err := req.Write(dst); err != nil {
logrus.Errorf("[%s][%s] write error: %s", destAddrPort, srcAddr, err.Error())
return err
@@ -166,10 +170,10 @@ func (r *Rewriter) ProxyHTTPOrRaw(dst net.Conn, src net.Conn, destAddrPort strin
return err
}
- statistics.AddStat(&statistics.StatRecord{
- Host: destAddrPort,
- OriginUA: originalUA,
- MockedUA: mockedUA,
+ statistics.AddRewriteRecord(&statistics.RewriteRecord{
+ Host: destAddrPort,
+ OriginalUA: originalUA,
+ MockedUA: mockedUA,
})
}
}
@@ -196,7 +200,7 @@ func (r *Rewriter) isHTTP(reader *bufio.Reader) (bool, error) {
// buildNewUA returns either a partial replacement (regex) or full overwrite.
func (r *Rewriter) buildNewUA(originUA string) string {
- if r.enablePartialReplace && r.uaRegex != nil {
+ if r.enablePartialReplace && r.uaRegex != nil && r.pattern != "" {
newUA, err := r.uaRegex.Replace(originUA, r.payloadUA, -1, -1)
if err != nil {
logrus.Errorf("User-Agent Replace Error: %s, use full overwrite", err.Error())
diff --git a/src/internal/server/socks5/socks5.go b/src/internal/server/socks5/socks5.go
index 07357fb..7ffdbe0 100644
--- a/src/internal/server/socks5/socks5.go
+++ b/src/internal/server/socks5/socks5.go
@@ -56,7 +56,7 @@ func (s *Server) Start() (err error) {
}
// Start statistics worker
- go statistics.StartStatWorker()
+ go statistics.StartRecorder()
var client net.Conn
for {
diff --git a/src/internal/statistics/pass.go b/src/internal/statistics/pass.go
new file mode 100644
index 0000000..78e575f
--- /dev/null
+++ b/src/internal/statistics/pass.go
@@ -0,0 +1,48 @@
+package statistics
+
+import (
+ "fmt"
+ "os"
+ "sort"
+
+ "github.com/sirupsen/logrus"
+)
+
+const passthroughStatsFile = "/var/log/ua3f/passthrough_stats"
+
+type PassThroughRecord struct {
+ Host string
+ UA string
+ Count int
+}
+
+var passThroughRecords = make(map[string]*PassThroughRecord)
+
+func AddPassThroughRecord(record *PassThroughRecord) {
+ select {
+ case passThroughRecordChan <- *record:
+ default:
+ }
+}
+
+func dumpPassThroughRecords() {
+ f, err := os.Create(passthroughStatsFile)
+ if err != nil {
+ logrus.Errorf("create stats file error: %v", err)
+ return
+ }
+ defer f.Close()
+
+ var statList []PassThroughRecord
+ for _, record := range passThroughRecords {
+ statList = append(statList, *record)
+ }
+ sort.SliceStable(statList, func(i, j int) bool {
+ return statList[i].Count > statList[j].Count
+ })
+
+ for _, record := range statList {
+ line := fmt.Sprintf("%s %d %s\n", record.Host, record.Count, record.UA)
+ f.WriteString(line)
+ }
+}
diff --git a/src/internal/statistics/rewrite.go b/src/internal/statistics/rewrite.go
new file mode 100644
index 0000000..dc340b0
--- /dev/null
+++ b/src/internal/statistics/rewrite.go
@@ -0,0 +1,49 @@
+package statistics
+
+import (
+ "fmt"
+ "os"
+ "sort"
+
+ "github.com/sirupsen/logrus"
+)
+
+const rewriteStatsFile = "/var/log/ua3f/rewrite_stats"
+
+type RewriteRecord struct {
+ Host string
+ Count int
+ OriginalUA string
+ MockedUA string
+}
+
+var rewriteRecords = make(map[string]*RewriteRecord)
+
+func AddRewriteRecord(record *RewriteRecord) {
+ select {
+ case rewriteRecordChan <- *record:
+ default:
+ }
+}
+
+func dumpRewriteRecords() {
+ f, err := os.Create(rewriteStatsFile)
+ if err != nil {
+ logrus.Errorf("create stats file error: %v", err)
+ return
+ }
+ defer f.Close()
+
+ var statList []RewriteRecord
+ for _, record := range rewriteRecords {
+ statList = append(statList, *record)
+ }
+ sort.SliceStable(statList, func(i, j int) bool {
+ return statList[i].Count > statList[j].Count
+ })
+
+ for _, record := range statList {
+ line := fmt.Sprintf("%s %d %sSEQSEQ%s\n", record.Host, record.Count, record.OriginalUA, record.MockedUA)
+ f.WriteString(line)
+ }
+}
diff --git a/src/internal/statistics/statistics.go b/src/internal/statistics/statistics.go
index 35cb9e7..69e40f2 100644
--- a/src/internal/statistics/statistics.go
+++ b/src/internal/statistics/statistics.go
@@ -1,78 +1,47 @@
package statistics
import (
- "fmt"
- "os"
- "sort"
"time"
-
- "github.com/sirupsen/logrus"
)
-type StatRecord struct {
- Host string
- Count int
- OriginUA string
- MockedUA string
-}
-
var (
- statChan = make(chan StatRecord, 3000)
- stats = make(map[string]*StatRecord)
+ rewriteRecordChan = make(chan RewriteRecord, 2000)
+ passThroughRecordChan = make(chan PassThroughRecord, 2000)
)
-const statsFile = "/var/log/ua3f/stats"
-
-func StartStatWorker() {
+func StartRecorder() {
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
select {
- case dest := <-statChan:
- if record, exists := stats[dest.Host]; exists {
- record.Count++
- record.OriginUA = dest.OriginUA
- record.MockedUA = dest.MockedUA
+ case record := <-rewriteRecordChan:
+ if r, exists := rewriteRecords[record.Host]; exists {
+ r.Count++
+ r.OriginalUA = record.OriginalUA
+ r.MockedUA = record.MockedUA
} else {
- stats[dest.Host] = &StatRecord{
- Host: dest.Host,
- Count: 1,
- OriginUA: dest.OriginUA,
- MockedUA: dest.MockedUA,
+ rewriteRecords[record.Host] = &RewriteRecord{
+ Host: record.Host,
+ Count: 1,
+ OriginalUA: record.OriginalUA,
+ MockedUA: record.MockedUA,
+ }
+ }
+ case record := <-passThroughRecordChan:
+ if r, exists := passThroughRecords[record.UA]; exists {
+ r.Count++
+ r.Host = record.Host
+ } else {
+ passThroughRecords[record.UA] = &PassThroughRecord{
+ Host: record.Host,
+ UA: record.UA,
+ Count: 1,
}
}
case <-ticker.C:
- dumpStatsToFile()
+ dumpRewriteRecords()
+ dumpPassThroughRecords()
}
}
}
-
-func AddStat(dest *StatRecord) {
- select {
- case statChan <- *dest:
- default:
- }
-}
-
-func dumpStatsToFile() {
- f, err := os.Create(statsFile)
- if err != nil {
- logrus.Errorf("create stats file error: %v", err)
- return
- }
- defer f.Close()
-
- var statList []StatRecord
- for _, record := range stats {
- statList = append(statList, *record)
- }
- sort.SliceStable(statList, func(i, j int) bool {
- return statList[i].Count > statList[j].Count
- })
-
- for _, record := range statList {
- line := fmt.Sprintf("%s %d %sSEQSEQ%s\n", record.Host, record.Count, record.OriginUA, record.MockedUA)
- f.WriteString(line)
- }
-}