diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..ed3d2a1
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,37 @@
+# This workflow will build a golang project
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
+
+name: CI
+
+on:
+ push:
+ branches: [ "main" ]
+ pull_request:
+ branches: [ "main" ]
+
+jobs:
+
+ build:
+ name: Build on ${{ matrix.os }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ os: [ubuntu-20.04, windows-2019]
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: 'recursive'
+ - name: Set up Go
+ uses: actions/setup-go@v4
+ with:
+ go-version: 1.20.10
+ - name: Install dependencies
+ run: go get .
+ - name: Build
+ shell: bash
+ run: |
+ if [ "${{ matrix.os }}" = "ubuntu-20.04" ]; then
+ go build -trimpath -ldflags "-s -w" -o httpdump httpdump.go
+ elif [ "${{ matrix.os }}" = "windows-2019" ]; then
+ go build -trimpath -ldflags "-s -w" -o httpdump.exe httpdump.go
+ fi
diff --git a/.github/workflows/release-setup.yml b/.github/workflows/release-setup.yml
new file mode 100644
index 0000000..5ec3d62
--- /dev/null
+++ b/.github/workflows/release-setup.yml
@@ -0,0 +1,26 @@
+name: Release Setup
+
+on:
+ workflow_call
+
+jobs:
+ release:
+ name: "Continuous Release"
+ runs-on: ubuntu-latest
+ steps:
+ - name: Job info
+ run: |
+ echo "GitHub Ref: ${{ github.ref }}"
+ - name: Delete old workflow runs
+ uses: Mattraks/delete-workflow-runs@main
+ with:
+ retain_days: 2
+ keep_minimum_runs: 2
+ - name: Automatic release
+ uses: "marvinpinto/action-automatic-releases@latest"
+ if: startsWith(github.ref, 'refs/heads/')
+ with:
+ repo_token: "${{ secrets.GITHUB_TOKEN }}"
+ automatic_release_tag: "continuous"
+ prerelease: true
+ title: "Continuous release"
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..4d215d0
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,87 @@
+# This workflow will build a golang project
+# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go
+
+name: Release
+
+on:
+ workflow_dispatch
+
+jobs:
+ setup:
+ name: Setup
+ uses: ./.github/workflows/release-setup.yml
+ build:
+ name: Build
+ runs-on: ${{ matrix.os }}
+ strategy:
+ matrix:
+ build: [linux, linux-aarch64, linux-armv7, linux-mipsle, windows]
+ include:
+ - build: linux
+ os: ubuntu-20.04
+ archive-name: httpdump-linux-amd64.tar.gz
+ - build: linux-aarch64
+ os: ubuntu-20.04
+ archive-name: httpdump-linux-aarch64.tar.gz
+ - build: linux-armv7
+ os: ubuntu-20.04
+ archive-name: httpdump-linux-armv7.tar.gz
+ - build: linux-mipsle
+ os: ubuntu-20.04
+ archive-name: httpdump-linux-mipsle.tar.gz
+ - build: windows
+ os: windows-2019
+ archive-name: httpdump-windows10-amd64.7z
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: 'recursive'
+ - name: Set up Go
+ uses: actions/setup-go@v4
+ with:
+ go-version: 1.20.10
+ - name: Install dependencies
+ run: go get .
+ - name: Build
+ shell: bash
+ run: |
+ if [ "${{ matrix.build }}" = "linux" ]; then
+ go build -trimpath -ldflags "-s -w" -o httpdump httpdump.go
+ elif [ "${{ matrix.build }}" = "linux-aarch64" ]; then
+ GOOS=linux GOARCH=arm64 go build -trimpath -ldflags "-s -w" -o httpdump httpdump.go
+ elif [ "${{ matrix.build }}" = "linux-armv7" ]; then
+ GOOS=linux GOARM=7 GOARCH=arm go build -trimpath -ldflags "-s -w" -o httpdump httpdump.go
+ elif [ "${{ matrix.build }}" = "linux-mipsle" ]; then
+ GOOS=linux GOARCH=mipsle go build -trimpath -ldflags "-s -w" -o httpdump httpdump.go
+ elif [ "${{ matrix.build }}" = "windows" ]; then
+ go build -trimpath -ldflags "-s -w" -o httpdump.exe httpdump.go
+ fi
+ - name: Build archive
+ shell: bash
+ run: |
+ mkdir archive
+ cp LICENSE README.md archive/
+ # ls -lR
+ if [ "${{ matrix.build }}" = "windows" ]; then
+ cp httpdump.exe ./archive/
+ cd archive
+ 7z a "${{ matrix.archive-name }}" LICENSE README.md httpdump.exe
+ else
+ cp httpdump ./archive/
+ cd archive
+ tar -czf "${{ matrix.archive-name }}" LICENSE README.md httpdump
+ fi
+ - name: Continuous release
+ uses: softprops/action-gh-release@v1
+ if: startsWith(github.ref, 'refs/heads/')
+ with:
+ prerelease: false
+ files: archive/${{ matrix.archive-name }}
+ tag_name: continuous
+
+ - if: startsWith(github.ref, 'refs/tags/')
+ name: Tagged release
+ uses: softprops/action-gh-release@v1
+ with:
+ files: archive/${{ matrix.archive-name }}
+ name: Release build (${{ github.ref_name }})
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b5961b9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+.idea/*
+!.idea/codeStyles
+!.idea/*.iml
+!.idea/modules.xml
+
+test.go
+httpdump
+httpdump.log
diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..4a883ee
--- /dev/null
+++ b/.idea/codeStyles/Project.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..79ee123
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/httpdump.iml b/.idea/httpdump.iml
new file mode 100644
index 0000000..25ed3f6
--- /dev/null
+++ b/.idea/httpdump.iml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..590354a
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.run/go build httpdump.go.run.xml b/.run/go build httpdump.go.run.xml
new file mode 100644
index 0000000..50e175f
--- /dev/null
+++ b/.run/go build httpdump.go.run.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..56fd1d9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 TLSLink
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ab42906
--- /dev/null
+++ b/README.md
@@ -0,0 +1,8 @@
+## httpdump
+
+```
+Usage: ./httpdump [options]
+Options:
+ -p string
+ http port (default "8080")
+```
\ No newline at end of file
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..5fd4a51
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,19 @@
+module httpdump
+
+go 1.21
+
+require github.com/gofiber/fiber/v2 v2.50.0
+
+require (
+ github.com/andybalholm/brotli v1.0.5 // indirect
+ github.com/google/uuid v1.3.1 // indirect
+ github.com/klauspost/compress v1.16.7 // indirect
+ github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mattn/go-isatty v0.0.19 // indirect
+ github.com/mattn/go-runewidth v0.0.15 // indirect
+ github.com/rivo/uniseg v0.2.0 // indirect
+ github.com/valyala/bytebufferpool v1.0.0 // indirect
+ github.com/valyala/fasthttp v1.50.0 // indirect
+ github.com/valyala/tcplisten v1.0.0 // indirect
+ golang.org/x/sys v0.13.0 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..7dfb28c
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,27 @@
+github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
+github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
+github.com/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw=
+github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw=
+github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
+github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
+github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
+github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M=
+github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
+github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
+github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
+golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/httpdump.go b/httpdump.go
new file mode 100644
index 0000000..5e3d282
--- /dev/null
+++ b/httpdump.go
@@ -0,0 +1,76 @@
+package main
+
+import (
+ "flag"
+ "fmt"
+ "github.com/gofiber/fiber/v2"
+ "httpdump/pkg/log"
+ "os"
+ "os/signal"
+ "syscall"
+)
+
+func main() {
+ port := flag.String("p", "8080", "http port")
+ respType := flag.String("t", "html", "response Content-Type: html, json, txt")
+ logPath := flag.String("l", "", "log path")
+
+ flag.Usage = func() {
+ fmt.Fprintf(os.Stderr, "Usage: %s [options]\n", os.Args[0])
+ fmt.Fprintf(os.Stderr, "Options:\n")
+ flag.PrintDefaults()
+ }
+ flag.Parse()
+
+ // init logger
+ log.Init(*logPath, "trace")
+
+ app := fiber.New(fiber.Config{
+ DisableStartupMessage: true,
+ DisableDefaultDate: true,
+ DisableHeaderNormalizing: true,
+ DisableDefaultContentType: true,
+ })
+ // Match any request
+ app.Use(func(c *fiber.Ctx) error {
+ log.Trace(c.IP(), c.Request().String())
+ err := c.Next()
+ // print response
+ // log.Debug(c.Response().String())
+ return err
+ })
+ app.Get("/", func(c *fiber.Ctx) error {
+ switch *respType {
+ case "json":
+ return c.JSON(fiber.Map{"hello": "world"})
+ case "txt":
+ c.Set("Content-Type", "text/plain; charset=utf-8")
+ return c.SendString("hello world")
+ default:
+ c.Set("Content-Type", "text/html; charset=utf-8")
+ return c.SendString("hello world")
+ }
+ })
+ // fmt.Println(*port)
+ go log.Fatal(app.Listen(fmt.Sprintf(":%s", *port)))
+
+ watchSignal()
+}
+
+func watchSignal() {
+ log.Info("Server pid:", os.Getpid())
+
+ sigs := make(chan os.Signal, 1)
+ // https://pkg.go.dev/os/signal
+ signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
+ for {
+ // 没有信号就阻塞,从而避免主协程退出
+ sig := <-sigs
+ log.Info("Get signal:", sig)
+ switch sig {
+ default:
+ log.Info("Stop")
+ return
+ }
+ }
+}
diff --git a/pkg/log/log.go b/pkg/log/log.go
new file mode 100644
index 0000000..da194da
--- /dev/null
+++ b/pkg/log/log.go
@@ -0,0 +1,163 @@
+package log
+
+import (
+ "fmt"
+ "log"
+ "os"
+ "path"
+ "strings"
+ "time"
+)
+
+const (
+ LevelTrace = iota
+ LevelDebug
+ LevelInfo
+ LevelWarn
+ LevelError
+ LevelFatal
+)
+
+var (
+ lw *logWriter
+ logger *log.Logger
+ logLevel int
+ levels map[int]string
+
+ dateFormat = "2006-01-02"
+ logName = "httpdump.log"
+)
+
+// 实现 os.Writer 接口
+type logWriter struct {
+ UseStdout bool
+ FileName string
+ File *os.File
+ NowDate string
+}
+
+// 实现日志文件的切割
+func (lw *logWriter) Write(p []byte) (n int, err error) {
+ if !lw.UseStdout {
+ date := time.Now().Format(dateFormat)
+ if lw.NowDate != date {
+ _ = lw.File.Close()
+ _ = os.Rename(lw.FileName, lw.FileName+"."+lw.NowDate)
+ lw.NowDate = date
+ lw.newFile()
+ }
+ }
+ return lw.File.Write(p)
+}
+
+// 创建新文件
+func (lw *logWriter) newFile() {
+ if lw.UseStdout {
+ lw.File = os.Stdout
+ return
+ }
+
+ f, err := os.OpenFile(lw.FileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
+ if err != nil {
+ panic(err)
+ }
+ lw.File = f
+}
+
+func Init(logPath, level string) {
+ // 初始化 logger
+ lw = &logWriter{
+ UseStdout: logPath == "",
+ FileName: path.Join(logPath, logName),
+ NowDate: time.Now().Format(dateFormat),
+ }
+
+ lw.newFile()
+ logLevel = logLevel2Int(level)
+ logger = log.New(lw, "", log.LstdFlags|log.Lshortfile)
+}
+
+func LoggerWriter() *logWriter {
+ return lw
+}
+
+// Logger 获取 log.Logger
+func Logger() *log.Logger {
+ return logger
+}
+
+func Level() int {
+ return logLevel
+}
+
+func logLevel2Int(l string) int {
+ levels = map[int]string{
+ LevelTrace: "Trace",
+ LevelDebug: "Debug",
+ LevelInfo: "Info",
+ LevelWarn: "Warn",
+ LevelError: "Error",
+ LevelFatal: "Fatal",
+ }
+ lvl := LevelInfo
+ for k, v := range levels {
+ if strings.ToLower(l) == strings.ToLower(v) {
+ lvl = k
+ }
+ }
+ return lvl
+}
+
+func output(l int, s ...interface{}) {
+ logger.SetPrefix(fmt.Sprintf("[%s] ", levels[l]))
+ _ = logger.Output(3, fmt.Sprintln(s...))
+}
+
+func Trace(v ...interface{}) {
+ l := LevelTrace
+ if logLevel > l {
+ return
+ }
+ output(l, v...)
+}
+
+func Debug(v ...interface{}) {
+ l := LevelDebug
+ if logLevel > l {
+ return
+ }
+ output(l, v...)
+}
+
+func Info(v ...interface{}) {
+ l := LevelInfo
+ if logLevel > l {
+ return
+ }
+ output(l, v...)
+}
+
+func Warn(v ...interface{}) {
+ l := LevelWarn
+ if logLevel > l {
+ return
+ }
+ output(l, v...)
+}
+
+func Error(v ...interface{}) {
+ l := LevelError
+ if logLevel > l {
+ return
+ }
+ output(l, v...)
+}
+
+func Fatal(v ...interface{}) {
+ l := LevelFatal
+ if logLevel > l {
+ return
+ }
+ output(l, v...)
+ os.Exit(1)
+}