From e789d40258bdac6583d5cec98dd3b5e29df70fbd Mon Sep 17 00:00:00 2001 From: Balakrishnan Balasubramanian Date: Tue, 17 Dec 2024 12:07:42 -0500 Subject: [PATCH 1/7] Add helper function to parse address --- anyhttp.go | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/anyhttp.go b/anyhttp.go index 5c06154..a05d983 100644 --- a/anyhttp.go +++ b/anyhttp.go @@ -7,11 +7,13 @@ import ( "io/fs" "net" "net/http" + "net/url" "os" "strconv" "strings" "sync" "syscall" + "time" ) // AddressType of the address passed @@ -97,6 +99,8 @@ type SysdConfig struct { CheckPID bool // Unsets the LISTEN* environment variables, so they don't get passed to any child processes UnsetEnv bool + // Shutdown http server if no requests received for below timeout + IdleTimeout *time.Duration } // DefaultSysdConfig has the default values for SysdConfig @@ -267,3 +271,121 @@ func UnsetSystemdListenVars() { _ = os.Unsetenv("LISTEN_FDS") _ = os.Unsetenv("LISTEN_FDNAMES") } + +func ParseAddress(addr string) (addrType AddressType, usc *UnixSocketConfig, sysc *SysdConfig, err error) { + // addrType = Unknown + usc = nil + sysc = nil + err = nil + u, err := url.Parse(addr) + if err != nil { + return TCP, nil, nil, nil + } + if u.Path == "unix" { + duc := DefaultUnixSocketConfig + usc = &duc + addrType = UnixSocket + for key, val := range u.Query() { + if key == "path" { + if len(val) != 1 { + err = fmt.Errorf("unix socket address error. Multiple path found: %v", val) + return + } + usc.SocketPath = val[0] + } else if key == "mode" { + if len(val) != 1 { + err = fmt.Errorf("unix socket address error. Multiple mode found: %v", val) + return + } + if _, serr := fmt.Sscanf(val[0], "%o", &usc.SocketMode); serr != nil { + err = fmt.Errorf("unix socket address error. Bad mode: %v, err: %w", val, serr) + return + } + } else if key == "remove_existing" { + if len(val) != 1 { + err = fmt.Errorf("unix socket address error. Multiple remove_existing found: %v", val) + return + } + if removeExisting, berr := strconv.ParseBool(val[0]); berr != nil { + err = fmt.Errorf("unix socket address error. Bad remove_existing: %v, err: %w", val, berr) + return + } else { + usc.RemoveExisting = removeExisting + } + } else { + err = fmt.Errorf("unix socket address error. Bad option; key: %v, val: %v", key, val) + return + } + } + if usc.SocketPath == "" { + err = fmt.Errorf("unix socket address error. Missing path; addr: %v", addr) + return + } + } else if u.Path == "sysd" { + dsc := DefaultSysdConfig + sysc = &dsc + addrType = SystemdFD + for key, val := range u.Query() { + if key == "name" { + if len(val) != 1 { + err = fmt.Errorf("systemd socket fd address error. Multiple name found: %v", val) + return + } + sysc.FDName = &val[0] + } else if key == "idx" { + if len(val) != 1 { + err = fmt.Errorf("systemd socket fd address error. Multiple idx found: %v", val) + return + } + if idx, ierr := strconv.Atoi(val[0]); ierr != nil { + err = fmt.Errorf("systemd socket fd address error. Bad idx: %v, err: %w", val, ierr) + return + } else { + sysc.FDIndex = &idx + } + } else if key == "check_pid" { + if len(val) != 1 { + err = fmt.Errorf("systemd socket fd address error. Multiple check_pid found: %v", val) + return + } + if checkPID, berr := strconv.ParseBool(val[0]); berr != nil { + err = fmt.Errorf("systemd socket fd address error. Bad check_pid: %v, err: %w", val, berr) + return + } else { + sysc.CheckPID = checkPID + } + } else if key == "unset_env" { + if len(val) != 1 { + err = fmt.Errorf("systemd socket fd address error. Multiple unset_env found: %v", val) + return + } + if unsetEnv, berr := strconv.ParseBool(val[0]); berr != nil { + err = fmt.Errorf("systemd socket fd address error. Bad unset_env: %v, err: %w", val, berr) + return + } else { + sysc.UnsetEnv = unsetEnv + } + } else if key == "idle_timeout" { + if len(val) != 1 { + err = fmt.Errorf("systemd socket fd address error. Multiple idle_timeout found: %v", val) + return + } + if timeout, terr := time.ParseDuration(val[0]); terr != nil { + err = fmt.Errorf("systemd socket fd address error. Bad idle_timeout: %v, err: %w", val, terr) + return + } else { + sysc.IdleTimeout = &timeout + } + } else { + err = fmt.Errorf("systemd socket fd address error. Bad option; key: %v, val: %v", key, val) + return + } + } + if (sysc.FDIndex == nil) == (sysc.FDName == nil) { + err = fmt.Errorf("systemd socket fd address error. Exactly only one of name and idx has to be set. name: %v, idx: %v", sysc.FDName, sysc.FDIndex) + return + } + } + // Just assume as TCP address + return TCP, nil, nil, nil +} -- 2.47.1 From 79256b044006e27f1e6e368e7bbe7c32c75ffb98 Mon Sep 17 00:00:00 2001 From: Balakrishnan Balasubramanian Date: Tue, 17 Dec 2024 13:21:37 -0500 Subject: [PATCH 2/7] update to new syntax --- anyhttp.go | 97 ++++++++++++++++++++++++++---------------------------- 1 file changed, 47 insertions(+), 50 deletions(-) diff --git a/anyhttp.go b/anyhttp.go index a05d983..dd1a74d 100644 --- a/anyhttp.go +++ b/anyhttp.go @@ -2,6 +2,7 @@ package anyhttp import ( + "context" "errors" "fmt" "io/fs" @@ -14,6 +15,8 @@ import ( "sync" "syscall" "time" + + "go.balki.me/anyhttp/idle" ) // AddressType of the address passed @@ -200,65 +203,59 @@ func (s *SysdConfig) GetListener() (net.Listener, error) { return nil, errors.New("neither FDIndex nor FDName set") } -// GetListener gets a unix or systemd socket listener -func GetListener(addr string) (AddressType, net.Listener, error) { - if strings.HasPrefix(addr, "unix/") { - usc := NewUnixSocketConfig(strings.TrimPrefix(addr, "unix/")) - l, err := usc.GetListener() - return UnixSocket, l, err - } - - if strings.HasPrefix(addr, "sysd/fdidx/") { - idx, err := strconv.Atoi(strings.TrimPrefix(addr, "sysd/fdidx/")) - if err != nil { - return Unknown, nil, fmt.Errorf("invalid fdidx, addr:%q err: %w", addr, err) - } - sysdc := NewSysDConfigWithFDIdx(idx) - l, err := sysdc.GetListener() - return SystemdFD, l, err - } - - if strings.HasPrefix(addr, "sysd/fdname/") { - sysdc := NewSysDConfigWithFDName(strings.TrimPrefix(addr, "sysd/fdname/")) - l, err := sysdc.GetListener() - return SystemdFD, l, err - } - - if port, err := strconv.Atoi(addr); err == nil { - if port > 0 && port < 65536 { - addr = fmt.Sprintf(":%v", port) - } else { - return Unknown, nil, fmt.Errorf("invalid port: %v", port) - } - } - - if addr == "" { - addr = ":http" - } - - l, err := net.Listen("tcp", addr) - return TCP, l, err -} - // Serve creates and serve a http server. -func Serve(addr string, h http.Handler) (AddressType, *http.Server, <-chan error, error) { - addrType, listener, err := GetListener(addr) +func Serve(addr string, h http.Handler) (addrType AddressType, srv *http.Server, idler idle.Idler, done <-chan error, err error) { + addrType, usc, sysc, err := ParseAddress(addr) if err != nil { - return addrType, nil, nil, err + return } - srv := &http.Server{Handler: h} - done := make(chan error) - go func() { - done <- srv.Serve(listener) - close(done) + + listener, err := func() (net.Listener, error) { + if usc != nil { + return usc.GetListener() + } else if sysc != nil { + return sysc.GetListener() + } else { + if addr == "" { + addr = ":http" + } + return net.Listen("tcp", addr) + } }() - return addrType, srv, done, nil + if err != nil { + return + } + errChan := make(chan error) + done = errChan + if addrType == SystemdFD && sysc.IdleTimeout != nil { + idler = idle.CreateIdler(*sysc.IdleTimeout) + srv = &http.Server{Handler: idle.WrapIdlerHandler(idler, h)} + waitErrChan := make(chan error) + go func() { + waitErrChan <- srv.Serve(listener) + close(waitErrChan) + }() + go func() { + select { + case err := <-waitErrChan: + errChan <- err + case <-idler.Chan(): + errChan <- srv.Shutdown(context.TODO()) + } + }() + } else { + srv = &http.Server{Handler: h} + go func() { + errChan <- srv.Serve(listener) + }() + } + return } // ListenAndServe is the drop-in replacement for `http.ListenAndServe`. // Supports unix and systemd sockets in addition func ListenAndServe(addr string, h http.Handler) error { - _, _, done, err := Serve(addr, h) + _, _, _, done, err := Serve(addr, h) if err != nil { return err } -- 2.47.1 From 47ee5fb4f4eda5f6c2fea9ca932296af359ed422 Mon Sep 17 00:00:00 2001 From: Balakrishnan Balasubramanian Date: Tue, 17 Dec 2024 18:16:38 -0500 Subject: [PATCH 3/7] remove channel close --- anyhttp.go | 1 - 1 file changed, 1 deletion(-) diff --git a/anyhttp.go b/anyhttp.go index dd1a74d..89f4e18 100644 --- a/anyhttp.go +++ b/anyhttp.go @@ -233,7 +233,6 @@ func Serve(addr string, h http.Handler) (addrType AddressType, srv *http.Server, waitErrChan := make(chan error) go func() { waitErrChan <- srv.Serve(listener) - close(waitErrChan) }() go func() { select { -- 2.47.1 From 7cab173c7d00c230bf3a8b8260d607b90b157a83 Mon Sep 17 00:00:00 2001 From: Balakrishnan Balasubramanian Date: Tue, 17 Dec 2024 19:36:31 -0500 Subject: [PATCH 4/7] fix ParseAddress --- anyhttp.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/anyhttp.go b/anyhttp.go index 89f4e18..743b064 100644 --- a/anyhttp.go +++ b/anyhttp.go @@ -381,7 +381,9 @@ func ParseAddress(addr string) (addrType AddressType, usc *UnixSocketConfig, sys err = fmt.Errorf("systemd socket fd address error. Exactly only one of name and idx has to be set. name: %v, idx: %v", sysc.FDName, sysc.FDIndex) return } + } else { + // Just assume as TCP address + return TCP, nil, nil, nil } - // Just assume as TCP address - return TCP, nil, nil, nil + return } -- 2.47.1 From b14072106a24814b11b44306f86e71974aa7b97b Mon Sep 17 00:00:00 2001 From: Balakrishnan Balasubramanian Date: Wed, 18 Dec 2024 15:38:52 -0500 Subject: [PATCH 5/7] Fix golint issues and add test --- anyhttp.go | 44 ++++++++-------- anyhttp_test.go | 132 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 23 deletions(-) create mode 100644 anyhttp_test.go diff --git a/anyhttp.go b/anyhttp.go index 743b064..7ec1f23 100644 --- a/anyhttp.go +++ b/anyhttp.go @@ -205,7 +205,7 @@ func (s *SysdConfig) GetListener() (net.Listener, error) { // Serve creates and serve a http server. func Serve(addr string, h http.Handler) (addrType AddressType, srv *http.Server, idler idle.Idler, done <-chan error, err error) { - addrType, usc, sysc, err := ParseAddress(addr) + addrType, usc, sysc, err := parseAddress(addr) if err != nil { return } @@ -215,12 +215,11 @@ func Serve(addr string, h http.Handler) (addrType AddressType, srv *http.Server, return usc.GetListener() } else if sysc != nil { return sysc.GetListener() - } else { - if addr == "" { - addr = ":http" - } - return net.Listen("tcp", addr) } + if addr == "" { + addr = ":http" + } + return net.Listen("tcp", addr) }() if err != nil { return @@ -268,8 +267,7 @@ func UnsetSystemdListenVars() { _ = os.Unsetenv("LISTEN_FDNAMES") } -func ParseAddress(addr string) (addrType AddressType, usc *UnixSocketConfig, sysc *SysdConfig, err error) { - // addrType = Unknown +func parseAddress(addr string) (addrType AddressType, usc *UnixSocketConfig, sysc *SysdConfig, err error) { usc = nil sysc = nil err = nil @@ -302,11 +300,11 @@ func ParseAddress(addr string) (addrType AddressType, usc *UnixSocketConfig, sys err = fmt.Errorf("unix socket address error. Multiple remove_existing found: %v", val) return } - if removeExisting, berr := strconv.ParseBool(val[0]); berr != nil { + if removeExisting, berr := strconv.ParseBool(val[0]); berr == nil { + usc.RemoveExisting = removeExisting + } else { err = fmt.Errorf("unix socket address error. Bad remove_existing: %v, err: %w", val, berr) return - } else { - usc.RemoveExisting = removeExisting } } else { err = fmt.Errorf("unix socket address error. Bad option; key: %v, val: %v", key, val) @@ -333,44 +331,44 @@ func ParseAddress(addr string) (addrType AddressType, usc *UnixSocketConfig, sys err = fmt.Errorf("systemd socket fd address error. Multiple idx found: %v", val) return } - if idx, ierr := strconv.Atoi(val[0]); ierr != nil { + if idx, ierr := strconv.Atoi(val[0]); ierr == nil { + sysc.FDIndex = &idx + } else { err = fmt.Errorf("systemd socket fd address error. Bad idx: %v, err: %w", val, ierr) return - } else { - sysc.FDIndex = &idx } } else if key == "check_pid" { if len(val) != 1 { err = fmt.Errorf("systemd socket fd address error. Multiple check_pid found: %v", val) return } - if checkPID, berr := strconv.ParseBool(val[0]); berr != nil { + if checkPID, berr := strconv.ParseBool(val[0]); berr == nil { + sysc.CheckPID = checkPID + } else { err = fmt.Errorf("systemd socket fd address error. Bad check_pid: %v, err: %w", val, berr) return - } else { - sysc.CheckPID = checkPID } } else if key == "unset_env" { if len(val) != 1 { err = fmt.Errorf("systemd socket fd address error. Multiple unset_env found: %v", val) return } - if unsetEnv, berr := strconv.ParseBool(val[0]); berr != nil { + if unsetEnv, berr := strconv.ParseBool(val[0]); berr == nil { + sysc.UnsetEnv = unsetEnv + } else { err = fmt.Errorf("systemd socket fd address error. Bad unset_env: %v, err: %w", val, berr) return - } else { - sysc.UnsetEnv = unsetEnv } } else if key == "idle_timeout" { if len(val) != 1 { err = fmt.Errorf("systemd socket fd address error. Multiple idle_timeout found: %v", val) return } - if timeout, terr := time.ParseDuration(val[0]); terr != nil { + if timeout, terr := time.ParseDuration(val[0]); terr == nil { + sysc.IdleTimeout = &timeout + } else { err = fmt.Errorf("systemd socket fd address error. Bad idle_timeout: %v, err: %w", val, terr) return - } else { - sysc.IdleTimeout = &timeout } } else { err = fmt.Errorf("systemd socket fd address error. Bad option; key: %v, val: %v", key, val) diff --git a/anyhttp_test.go b/anyhttp_test.go new file mode 100644 index 0000000..74f58ad --- /dev/null +++ b/anyhttp_test.go @@ -0,0 +1,132 @@ +package anyhttp + +import ( + "encoding/json" + "testing" + "time" +) + +func Test_parseAddress(t *testing.T) { + tests := []struct { + name string // description of this test case + // Named input parameters for target function. + addr string + wantAddrType AddressType + wantUsc *UnixSocketConfig + wantSysc *SysdConfig + wantErr bool + }{ + { + name: "tcp port", + addr: ":8080", + wantAddrType: TCP, + wantUsc: nil, + wantSysc: nil, + wantErr: false, + }, + { + name: "unix address", + addr: "unix?path=/run/foo.sock&mode=660", + wantAddrType: UnixSocket, + wantUsc: &UnixSocketConfig{ + SocketPath: "/run/foo.sock", + SocketMode: 0660, + RemoveExisting: true, + }, + wantSysc: nil, + wantErr: false, + }, + { + name: "systemd address", + addr: "sysd?name=foo.socket", + wantAddrType: SystemdFD, + wantUsc: nil, + wantSysc: &SysdConfig{ + FDIndex: nil, + FDName: ptr("foo.socket"), + CheckPID: true, + UnsetEnv: true, + IdleTimeout: nil, + }, + wantErr: false, + }, + { + name: "systemd address with index", + addr: "sysd?idx=0&idle_timeout=30m", + wantAddrType: SystemdFD, + wantUsc: nil, + wantSysc: &SysdConfig{ + FDIndex: ptr(0), + FDName: nil, + CheckPID: true, + UnsetEnv: true, + IdleTimeout: ptr(30 * time.Minute), + }, + wantErr: false, + }, + { + name: "systemd address. Bad example", + addr: "sysd?idx=0&idle_timeout=30m&name=foo", + wantAddrType: SystemdFD, + wantUsc: nil, + wantSysc: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotAddrType, gotUsc, gotSysc, gotErr := parseAddress(tt.addr) + if gotErr != nil { + if !tt.wantErr { + t.Errorf("parseAddress() failed: %v", gotErr) + } + return + } + if tt.wantErr { + t.Fatal("parseAddress() succeeded unexpectedly") + } + + if gotAddrType != tt.wantAddrType { + t.Errorf("parseAddress() addrType = %v, want %v", gotAddrType, tt.wantAddrType) + } + + if !check(gotUsc, tt.wantUsc) { + t.Errorf("parseAddress() Usc = %v, want %v", gotUsc, tt.wantUsc) + } + if !check(gotSysc, tt.wantSysc) { + if (gotSysc == nil || tt.wantSysc == nil) || + !(check(gotSysc.FDIndex, tt.wantSysc.FDIndex) && + check(gotSysc.FDName, tt.wantSysc.FDName) && + check(gotSysc.IdleTimeout, tt.wantSysc.IdleTimeout)) { + t.Errorf("parseAddress() Sysc = %v, want %v", asJSON(gotSysc), asJSON(tt.wantSysc)) + } + } + }) + } +} + +// Helpers + +// print value instead of pointer +func asJSON[T any](val T) string { + op, err := json.Marshal(val) + if err != nil { + return err.Error() + } + return string(op) +} + +func ptr[T any](val T) *T { + return &val +} + +// nil safe equal check +func check[T comparable](got, want *T) bool { + if (got == nil) != (want == nil) { + return false + } + if got == nil { + return true + } + return *got == *want +} -- 2.47.1 From 06e51af6283559b7491dc8ed91857611ebfaa4e0 Mon Sep 17 00:00:00 2001 From: Balakrishnan Balasubramanian Date: Wed, 18 Dec 2024 15:45:36 -0500 Subject: [PATCH 6/7] do dupe keys check only once --- anyhttp.go | 40 ++++++++-------------------------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/anyhttp.go b/anyhttp.go index 7ec1f23..789b279 100644 --- a/anyhttp.go +++ b/anyhttp.go @@ -280,26 +280,18 @@ func parseAddress(addr string) (addrType AddressType, usc *UnixSocketConfig, sys usc = &duc addrType = UnixSocket for key, val := range u.Query() { + if len(val) != 1 { + err = fmt.Errorf("unix socket address error. Multiple %v found: %v", key, val) + return + } if key == "path" { - if len(val) != 1 { - err = fmt.Errorf("unix socket address error. Multiple path found: %v", val) - return - } usc.SocketPath = val[0] } else if key == "mode" { - if len(val) != 1 { - err = fmt.Errorf("unix socket address error. Multiple mode found: %v", val) - return - } if _, serr := fmt.Sscanf(val[0], "%o", &usc.SocketMode); serr != nil { err = fmt.Errorf("unix socket address error. Bad mode: %v, err: %w", val, serr) return } } else if key == "remove_existing" { - if len(val) != 1 { - err = fmt.Errorf("unix socket address error. Multiple remove_existing found: %v", val) - return - } if removeExisting, berr := strconv.ParseBool(val[0]); berr == nil { usc.RemoveExisting = removeExisting } else { @@ -320,17 +312,13 @@ func parseAddress(addr string) (addrType AddressType, usc *UnixSocketConfig, sys sysc = &dsc addrType = SystemdFD for key, val := range u.Query() { + if len(val) != 1 { + err = fmt.Errorf("systemd socket fd address error. Multiple %v found: %v", key, val) + return + } if key == "name" { - if len(val) != 1 { - err = fmt.Errorf("systemd socket fd address error. Multiple name found: %v", val) - return - } sysc.FDName = &val[0] } else if key == "idx" { - if len(val) != 1 { - err = fmt.Errorf("systemd socket fd address error. Multiple idx found: %v", val) - return - } if idx, ierr := strconv.Atoi(val[0]); ierr == nil { sysc.FDIndex = &idx } else { @@ -338,10 +326,6 @@ func parseAddress(addr string) (addrType AddressType, usc *UnixSocketConfig, sys return } } else if key == "check_pid" { - if len(val) != 1 { - err = fmt.Errorf("systemd socket fd address error. Multiple check_pid found: %v", val) - return - } if checkPID, berr := strconv.ParseBool(val[0]); berr == nil { sysc.CheckPID = checkPID } else { @@ -349,10 +333,6 @@ func parseAddress(addr string) (addrType AddressType, usc *UnixSocketConfig, sys return } } else if key == "unset_env" { - if len(val) != 1 { - err = fmt.Errorf("systemd socket fd address error. Multiple unset_env found: %v", val) - return - } if unsetEnv, berr := strconv.ParseBool(val[0]); berr == nil { sysc.UnsetEnv = unsetEnv } else { @@ -360,10 +340,6 @@ func parseAddress(addr string) (addrType AddressType, usc *UnixSocketConfig, sys return } } else if key == "idle_timeout" { - if len(val) != 1 { - err = fmt.Errorf("systemd socket fd address error. Multiple idle_timeout found: %v", val) - return - } if timeout, terr := time.ParseDuration(val[0]); terr == nil { sysc.IdleTimeout = &timeout } else { -- 2.47.1 From 74efe7c80801c3578d38f56dc2addf198ddb397b Mon Sep 17 00:00:00 2001 From: Balakrishnan Balasubramanian Date: Thu, 19 Dec 2024 09:46:36 -0500 Subject: [PATCH 7/7] update README --- README.md | 59 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 35950d4..8e15909 100644 --- a/README.md +++ b/README.md @@ -17,56 +17,57 @@ Just replace `http.ListenAndServe` with `anyhttp.ListenAndServe`. Syntax - unix/ + unix?path=&mode=&remove_existing= Examples - unix/relative/path.sock - unix//var/run/app/absolutepath.sock + unix?path=relative/path.sock + unix?path=/var/run/app/absolutepath.sock + unix?path=/run/app.sock&mode=600&remove_existing=no + +| option | description | default | +|-----------------|------------------------------------------------|----------| +| path | path to unix socket | Required | +| mode | socket file mode | 666 | +| remove_existing | Whether to remove existing socket file or fail | true | ### Systemd Socket activated fd: Syntax - sysd/fdidx/ - sysd/fdname/ + sysd?idx=&name=&check_pid=&unset_env=&idle_timeout= + +Only one of `idx` or `name` has to be set Examples: # First (or only) socket fd passed to app - sysd/fdidx/0 + sysd?idx=0 # Socket with FileDescriptorName - sysd/fdname/myapp + sysd?name=myapp - # Using default name - sysd/fdname/myapp.socket + # Using default name and auto shutdown if no requests received in last 30 minutes + sysd?name=myapp.socket&idle_timeout=30m -### TCP port +| option | description | default | +|--------------|--------------------------------------------------------------------------------------------|------------------| +| name | Name configured via FileDescriptorName or socket file name | Required | +| idx | FD Index. Actual fd num will be 3 + idx | Required | +| idle_timeout | time to wait before shutdown. [syntax][0] | no auto shutdown | +| check_pid | Check process PID matches LISTEN_PID | true | +| unset_env | Unsets the LISTEN\* environment variables, so they don't get passed to any child processes | true | -If the address is a number less than 65536, it is assumed as a port and passed -as `http.ListenAndServe(":",...)` Anything else is directly passed to -`http.ListenAndServe` as well. Below examples should work +### TCP + +If the address is not one of above, it is assumed to be tcp and passed to `http.ListenAndServe`. + +Examples: :http :8888 127.0.0.1:8080 -## Idle server auto shutdown - -When using systemd socket activation, idle servers can be shut down to save on -resources. They will be restarted with socket activation when new request -arrives. Quick example for the case. (Error checking skipped for brevity) - -```go -addrType, httpServer, done, _ := anyhttp.Serve(addr, idle.WrapHandler(nil)) -if addrType == anyhttp.SystemdFD { - idle.Wait(30 * time.Minute) - httpServer.Shutdown(context.TODO()) -} -<-done -``` - ## Documentation https://pkg.go.dev/go.balki.me/anyhttp @@ -75,3 +76,5 @@ https://pkg.go.dev/go.balki.me/anyhttp * https://gist.github.com/teknoraver/5ffacb8757330715bcbcc90e6d46ac74#file-unixhttpd-go * https://github.com/coreos/go-systemd/tree/main/activation + +[0]: https://pkg.go.dev/time#ParseDuration -- 2.47.1