diff --git a/PKGBUILD b/PKGBUILD index cd0e753..7cf799f 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -62,18 +62,18 @@ source=( 'sysusers.conf' 'tmpfiles.conf' 'use-fhs-directories.patch' - 'support-unix-sockets.patch' + 'modded.patch' ) sha512sums=('26ab454e2eb6894e4084a1f6a71d14b3279a3ae8456e8bbec48f133b889f5b2f20d8f4bc761bfe7d6c3fa14b49b69c11cb7162187700b527bb96873e89442d0f' '2ff5499a31e12733cb20a9261942ed135fbac327d5a836b8955f3e86c009a603cf965440d9dbe6db64b80d0f652ba56faddb0ef398393b72474d8cf6c438ab80' 'b89fad3073e140f17167515b38942e5b5e2bc2aee03c484e1bb7cf6444f86cb1e2a13a60b101e04d22633d348be073ca26cd309da4746e5062c12b4f3ce4b38a' '913a5209487aba06bf1d8ac7c02506a05d01a8e12f172666c84bf6870d6237640d4745617b0f07ea8c9dcf665f4e0d24a0aabef31611909f7e9384ed6e7b7e77' - '5cd0f4f325c452f311e5187147a0d91bae5b2537f329a087e6bc8618317021dbf2d80d7c42d135946ce2399223bebfa4988d856bdba730a20084ce9c21f77990') + '6c9a529ad0472d25500bb77bc9b6f268c01cef61559f5d9b26b192e84bd987d9eb22c6c5dee1dc7f60a5bb99513aff93f241e1e30478f81531fa53f55be442c6') b2sums=('3cff26624bd7231018accc002d9292fcde6bb12f91d80faf473fb96fd49248f490f9e62aa724deaab4e22445c4a2865dcc2b38d52f2b5be75feb2c211100689d' 'ccf672731b88fc6700b0b81737790365e1eea0066bad1bbf6b13dac1e5b42af69063838efca47a6d9c16ee3f6308e2e23b92cf79d4226cd88f8551fb7361649b' '4f65af952441c0f54bb32049a149675e207f8993678423d369c4095c57476464614ac720eccc64d7a93a81268ad7ca41cae75ca7211bd7b78f9035f6e5341f04' '9edd4520fb99856feb82d01935588add7f805aa180f2ed0fe169cb26576bc2e1d2c1e6ab11604d977cec6a4ad8f1d5be1413e1a366de59b89c5b869136538f8c' - 'a2ae0a592608bb66784c704a076ceacd3f96b81006354a02da33d1292125f5e4e96c35472206cd2c998a9bc0eb2f191ccc66be69eb1397d8aa3eee145c9e7a7a') + 'b92f9b3b10109c2f63099c12c38c8615dc9b22f846ba15ec4c004069a7291f166cd4cb6ebf820f62dbf934a3388a767feaa3fdf8b47cf0a04532b182caaec149') prepare() { cd "$pkgname" @@ -81,7 +81,7 @@ prepare() { # create directory for build output mkdir build - patch -p1 -i "$srcdir/support-unix-sockets.patch" + patch -p1 -i "$srcdir/modded.patch" # download dependencies export GOPATH="${srcdir}" go mod download diff --git a/support-unix-sockets.patch b/modded.patch similarity index 79% rename from support-unix-sockets.patch rename to modded.patch index c46558d..2e1abcb 100644 --- a/support-unix-sockets.patch +++ b/modded.patch @@ -1,11 +1,3 @@ -From 81a5c46ffe501341d942a2be7931b7c6b5ce9571 Mon Sep 17 00:00:00 2001 -From: Balakrishnan Balasubramanian -Date: Thu, 9 May 2024 00:04:42 -0400 -Subject: [PATCH 1/2] Support listening on unix sockets - -1. bind-address now accepts unix socket paths -2. Add config option trusted-platform - diff --git a/example/config.yaml b/example/config.yaml index 2b3a873fb..52615a345 100644 --- a/example/config.yaml @@ -49,8 +41,33 @@ index 2b3a873fb..52615a345 100644 # Default: "" db-address: "" +diff --git a/go.mod b/go.mod +index 7d15b19dd..beb7b0235 100644 +--- a/go.mod ++++ b/go.mod +@@ -72,6 +72,7 @@ require ( + github.com/uptrace/bun/extra/bunotel v1.2.11 + github.com/wagslane/go-password-validator v0.3.0 + github.com/yuin/goldmark v1.7.8 ++ go.balki.me/anyhttp v0.5.0 + go.opentelemetry.io/otel v1.34.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 +diff --git a/go.sum b/go.sum +index 24176856b..a9680692a 100644 +--- a/go.sum ++++ b/go.sum +@@ -620,6 +620,8 @@ github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= + github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= + gitlab.com/NyaaaWhatsUpDoc/sqlite v1.35.0-concurrency-workaround h1:rSPHdoNXzXyWQUUeMEy8pdOFn8lH7XqdBRTS9G+jdTg= + gitlab.com/NyaaaWhatsUpDoc/sqlite v1.35.0-concurrency-workaround/go.mod h1:9cr2sicr7jIaWTBKQmAxQLfBv9LL0su4ZTEV+utt3ic= ++go.balki.me/anyhttp v0.5.0 h1:uys0oRciBpZfwtxXAevScKy6amIQBXyDrcV0EtGF5zo= ++go.balki.me/anyhttp v0.5.0/go.mod h1:JhfekOIjgVODoVqUCficjpIgmB3wwlB7jhN0eN2EZ/s= + go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= + go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= + go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= diff --git a/internal/config/config.go b/internal/config/config.go -index 8ce2105b4..fa90aad80 100644 +index 8ce2105b4..93ad8d7b0 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -59,6 +59,7 @@ type Configuration struct { @@ -61,10 +78,39 @@ index 8ce2105b4..fa90aad80 100644 SoftwareVersion string `name:"software-version" usage:""` DbType string `name:"db-type" usage:"Database type: eg., postgres"` +@@ -194,6 +195,8 @@ type Configuration struct { + AdminMediaListRemoteOnly bool `name:"remote-only" usage:"list only remote attachments/emojis; if specified then local-only cannot also be true"` + + RequestIDHeader string `name:"request-id-header" usage:"Header to extract the Request ID from. Eg.,'X-Request-Id'."` ++ ++ KalaclistaAllowedUnauthorizedGet bool `name:"kalaclista-allowed-unauthorized-get" usage:"unlock AUTHOZIED_FETCH (aka Secure mode in Mastodon) mode."` + } + + type HTTPClientConfiguration struct { +diff --git a/internal/config/defaults.go b/internal/config/defaults.go +index 78a8230d5..b8b2ff5f2 100644 +--- a/internal/config/defaults.go ++++ b/internal/config/defaults.go +@@ -233,4 +233,6 @@ + RequestIDHeader: "X-Request-Id", + + LogClientIP: true, ++ ++ KalaclistaAllowedUnauthorizedGet: false, + } diff --git a/internal/config/helpers.gen.go b/internal/config/helpers.gen.go -index 156c19fd5..a3c09fe8b 100644 +index 156c19fd5..ab12c44be 100644 --- a/internal/config/helpers.gen.go +++ b/internal/config/helpers.gen.go +@@ -2,7 +2,7 @@ + // GoToSocial + // Copyright (C) GoToSocial Authors admin@gotosocial.org + // SPDX-License-Identifier: AGPL-3.0-or-later +-// ++// + // This program is free software: you can redistribute it and/or modify + // it under the terms of the GNU Affero General Public License as published by + // the Free Software Foundation, either version 3 of the License, or @@ -351,6 +351,31 @@ func GetTrustedProxies() []string { return global.GetTrustedProxies() } // SetTrustedProxies safely sets the value for global configuration 'TrustedProxies' field func SetTrustedProxies(v []string) { global.SetTrustedProxies(v) } @@ -97,8 +143,174 @@ index 156c19fd5..a3c09fe8b 100644 // GetSoftwareVersion safely fetches the Configuration value for state's 'SoftwareVersion' field func (st *ConfigState) GetSoftwareVersion() (v string) { st.mutex.RLock() +@@ -3328,19 +3353,13 @@ func (st *ConfigState) SetCacheConversationLastStatusIDsMemRatio(v float64) { + } + + // CacheConversationLastStatusIDsMemRatioFlag returns the flag name for the 'Cache.ConversationLastStatusIDsMemRatio' field +-func CacheConversationLastStatusIDsMemRatioFlag() string { +- return "cache-conversation-last-status-ids-mem-ratio" +-} ++func CacheConversationLastStatusIDsMemRatioFlag() string { return "cache-conversation-last-status-ids-mem-ratio" } + + // GetCacheConversationLastStatusIDsMemRatio safely fetches the value for global configuration 'Cache.ConversationLastStatusIDsMemRatio' field +-func GetCacheConversationLastStatusIDsMemRatio() float64 { +- return global.GetCacheConversationLastStatusIDsMemRatio() +-} ++func GetCacheConversationLastStatusIDsMemRatio() float64 { return global.GetCacheConversationLastStatusIDsMemRatio() } + + // SetCacheConversationLastStatusIDsMemRatio safely sets the value for global configuration 'Cache.ConversationLastStatusIDsMemRatio' field +-func SetCacheConversationLastStatusIDsMemRatio(v float64) { +- global.SetCacheConversationLastStatusIDsMemRatio(v) +-} ++func SetCacheConversationLastStatusIDsMemRatio(v float64) { global.SetCacheConversationLastStatusIDsMemRatio(v) } + + // GetCacheDomainPermissionDraftMemRation safely fetches the Configuration value for state's 'Cache.DomainPermissionDraftMemRation' field + func (st *ConfigState) GetCacheDomainPermissionDraftMemRation() (v float64) { +@@ -4686,3 +4705,29 @@ func GetRequestIDHeader() string { return global.GetRequestIDHeader() } + + // SetRequestIDHeader safely sets the value for global configuration 'RequestIDHeader' field + func SetRequestIDHeader(v string) { global.SetRequestIDHeader(v) } ++ ++// GetKalaclistaAllowedUnauthorizedGet safely fetches the Configuration value for state's 'KalaclistaAllowedUnauthorizedGet' field ++func (st *ConfigState) GetKalaclistaAllowedUnauthorizedGet() (v bool) { ++ st.mutex.RLock() ++ v = st.config.KalaclistaAllowedUnauthorizedGet ++ st.mutex.RUnlock() ++ return ++} ++ ++// SetKalaclistaAllowedUnauthorizedGet safely sets the Configuration value for state's 'KalaclistaAllowedUnauthorizedGet' field ++func (st *ConfigState) SetKalaclistaAllowedUnauthorizedGet(v bool) { ++ st.mutex.Lock() ++ defer st.mutex.Unlock() ++ st.config.KalaclistaAllowedUnauthorizedGet = v ++ st.reloadToViper() ++} ++ ++// KalaclistaAllowedUnauthorizedGetFlag returns the flag name for the 'KalaclistaAllowedUnauthorizedGet' field ++func KalaclistaAllowedUnauthorizedGetFlag() string { return "kalaclista-allowed-unauthorized-get" } ++ ++// GetKalaclistaAllowedUnauthorizedGet safely fetches the value for global configuration 'KalaclistaAllowedUnauthorizedGet' field ++func GetKalaclistaAllowedUnauthorizedGet() bool { return global.GetKalaclistaAllowedUnauthorizedGet() } ++ ++// SetKalaclistaAllowedUnauthorizedGet safely sets the value for global configuration 'KalaclistaAllowedUnauthorizedGet' field ++func SetKalaclistaAllowedUnauthorizedGet(v bool) { global.SetKalaclistaAllowedUnauthorizedGet(v) } ++ +diff --git a/internal/middleware/signaturecheck.go b/internal/middleware/signaturecheck.go +index ea63ec4f0..cb8b9df9a 100644 +--- a/internal/middleware/signaturecheck.go ++++ b/internal/middleware/signaturecheck.go +@@ -33,7 +33,8 @@ + sigHeader = string(httpsig.Signature) + authHeader = string(httpsig.Authorization) + // untyped error returned by httpsig when no signature is present +- noSigError = "neither \"" + sigHeader + "\" nor \"" + authHeader + "\" have signature parameters" ++ noSigError = "neither \"" + sigHeader + "\" nor \"" + authHeader + "\" have signature parameters" ++ bothSigError = "both \"" + sigHeader + "\" and \"" + authHeader + "\" have signature parameters" + ) + + // SignatureCheck returns a gin middleware for checking http signatures. +@@ -54,6 +55,12 @@ func SignatureCheck(uriBlocked func(context.Context, *url.URL) (bool, error)) fu + // Create the signature verifier from the request; + // this will error if the request wasn't signed. + verifier, err := httpsig.NewVerifier(c.Request) ++ if err != nil && err.Error() == bothSigError { ++ log.Debugf(ctx, "this request has both http signature headers, but fix it: %s", err) ++ c.Request.Header.Del(authHeader) ++ verifier, err = httpsig.NewVerifier(c.Request) ++ } ++ + if err != nil { + // Only actually *abort* the request with 401 + // if a signature was present but malformed. +diff --git a/internal/processing/fedi/common.go b/internal/processing/fedi/common.go +index 1a4d38bc1..1d62aacdb 100644 +--- a/internal/processing/fedi/common.go ++++ b/internal/processing/fedi/common.go +@@ -20,8 +20,10 @@ + import ( + "context" + "errors" ++ "net/http" + "net/url" + ++ "github.com/superseriousbusiness/gotosocial/internal/config" + "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/gtserror" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" +@@ -51,6 +53,12 @@ func (p *Processor) authenticate(ctx context.Context, requestedUser string) (*co + // get requesting account, dereferencing if necessary. + pubKeyAuth, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, requestedUser) + if errWithCode != nil { ++ if config.GetKalaclistaAllowedUnauthorizedGet() && errWithCode.Code() == http.StatusUnauthorized { ++ return &commonAuth{ ++ receivingAcct: receiver, ++ }, nil ++ } ++ + return nil, errWithCode + } + +diff --git a/internal/processing/fedi/emoji.go b/internal/processing/fedi/emoji.go +index 9ac0ea244..446e496c0 100644 +--- a/internal/processing/fedi/emoji.go ++++ b/internal/processing/fedi/emoji.go +@@ -20,15 +20,19 @@ + import ( + "context" + "fmt" ++ "net/http" + + "github.com/superseriousbusiness/gotosocial/internal/ap" ++ "github.com/superseriousbusiness/gotosocial/internal/config" + "github.com/superseriousbusiness/gotosocial/internal/gtserror" + ) + + // EmojiGet handles the GET for a federated emoji originating from this instance. + func (p *Processor) EmojiGet(ctx context.Context, requestedEmojiID string) (interface{}, gtserror.WithCode) { + if _, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, ""); errWithCode != nil { +- return nil, errWithCode ++ if !(config.GetKalaclistaAllowedUnauthorizedGet() && errWithCode.Code() == http.StatusUnauthorized) { ++ return nil, errWithCode ++ } + } + + requestedEmoji, err := p.state.DB.GetEmojiByID(ctx, requestedEmojiID) +diff --git a/internal/processing/fedi/user.go b/internal/processing/fedi/user.go +index 79c1b4fdb..b980161c9 100644 +--- a/internal/processing/fedi/user.go ++++ b/internal/processing/fedi/user.go +@@ -21,9 +21,11 @@ + "context" + "errors" + "fmt" ++ "net/http" + "net/url" + + "github.com/superseriousbusiness/gotosocial/internal/ap" ++ "github.com/superseriousbusiness/gotosocial/internal/config" + "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/gtserror" + "github.com/superseriousbusiness/gotosocial/internal/uris" +@@ -67,6 +69,16 @@ func (p *Processor) UserGet(ctx context.Context, requestedUsername string, reque + // we can serve a more complete profile. + pubKeyAuth, errWithCode := p.federator.AuthenticateFederatedRequest(ctx, requestedUsername) + if errWithCode != nil { ++ // kalaclista modded: unauthorized-fetch ++ if config.GetKalaclistaAllowedUnauthorizedGet() && errWithCode.Code() == http.StatusUnauthorized { ++ person, err := p.converter.AccountToAS(ctx, receiver) ++ if err != nil { ++ err := gtserror.Newf("error converting account: %w", err) ++ return nil, gtserror.NewErrorInternalError(err) ++ } ++ return data(person) ++ } ++ + return nil, errWithCode // likely 401 + } + diff --git a/internal/router/router.go b/internal/router/router.go -index c2bf18b4f..73bfc8e96 100644 +index c2bf18b4f..97ee65847 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -23,6 +23,7 @@ @@ -142,9 +354,9 @@ index c2bf18b4f..73bfc8e96 100644 } + // TLS handled by reverse proxy connecting using unix socket -+ case strings.HasPrefix(bindAddr, "unix/"): ++ case strings.HasPrefix(bindAddr, "unix?"): + listen, err = func() (func() error, error) { -+ _, listener, err := anyhttp.GetListener(bindAddr) ++ listener, _, _, err := anyhttp.GetListener(bindAddr) + if err != nil { + return nil, err + } @@ -365,10 +577,10 @@ index 000000000..ad2653ac1 + limitations under the License. diff --git a/vendor/go.balki.me/anyhttp/README.md b/vendor/go.balki.me/anyhttp/README.md new file mode 100644 -index 000000000..7e9b11922 +index 000000000..85bce6fc3 --- /dev/null +++ b/vendor/go.balki.me/anyhttp/README.md -@@ -0,0 +1,77 @@ +@@ -0,0 +1,80 @@ +Create http server listening on unix sockets and systemd socket activated fds + +## Quick Usage @@ -388,56 +600,57 @@ index 000000000..7e9b11922 + +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=false ++ ++| 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 @@ -446,35 +659,42 @@ index 000000000..7e9b11922 + + * 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 diff --git a/vendor/go.balki.me/anyhttp/anyhttp.go b/vendor/go.balki.me/anyhttp/anyhttp.go new file mode 100644 -index 000000000..5c0615442 +index 000000000..8d316a78f --- /dev/null +++ b/vendor/go.balki.me/anyhttp/anyhttp.go -@@ -0,0 +1,269 @@ +@@ -0,0 +1,440 @@ +// Package anyhttp has helpers to serve http from unix sockets and systemd socket activated fds +package anyhttp + +import ( ++ "context" + "errors" + "fmt" + "io/fs" + "net" + "net/http" ++ "net/url" + "os" + "strconv" + "strings" + "sync" + "syscall" ++ "time" ++ ++ "go.balki.me/anyhttp/idle" +) + +// AddressType of the address passed +type AddressType string + +var ( -+ // UnixSocket - address is a unix socket, e.g. unix//run/foo.sock ++ // UnixSocket - address is a unix socket, e.g. unix?path=/run/foo.sock + UnixSocket AddressType = "UnixSocket" -+ // SystemdFD - address is a systemd fd, e.g. sysd/fdname/myapp.socket ++ // SystemdFD - address is a systemd fd, e.g. sysd?name=myapp.socket + SystemdFD AddressType = "SystemdFD" + // TCP - address is a TCP address, e.g. :1234 + TCP AddressType = "TCP" @@ -551,6 +771,8 @@ index 000000000..5c0615442 + 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 @@ -650,353 +872,34 @@ index 000000000..5c0615442 + 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) -+ if err != nil { -+ return addrType, nil, nil, err -+ } -+ srv := &http.Server{Handler: h} -+ done := make(chan error) -+ go func() { -+ done <- srv.Serve(listener) -+ close(done) -+ }() -+ return addrType, srv, done, nil -+} -+ -+// 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) -+ if err != nil { -+ return err -+ } -+ return <-done -+} -+ -+// UnsetSystemdListenVars unsets the LISTEN* environment variables so they are not passed to any child processes -+func UnsetSystemdListenVars() { -+ _ = os.Unsetenv("LISTEN_PID") -+ _ = os.Unsetenv("LISTEN_FDS") -+ _ = os.Unsetenv("LISTEN_FDNAMES") -+} -diff --git a/vendor/modules.txt b/vendor/modules.txt -index 8c52d8949..dfb915c88 100644 ---- a/vendor/modules.txt -+++ b/vendor/modules.txt -@@ -961,6 +961,9 @@ github.com/yuin/goldmark/renderer - github.com/yuin/goldmark/renderer/html - github.com/yuin/goldmark/text - github.com/yuin/goldmark/util -+# go.balki.me/anyhttp v0.3.0 -+## explicit; go 1.20 -+go.balki.me/anyhttp - # go.mongodb.org/mongo-driver v1.14.0 - ## explicit; go 1.18 - go.mongodb.org/mongo-driver/bson --- -2.48.1 - - -From d2de56e788b1660b404b50e99371937e9aa5128c Mon Sep 17 00:00:00 2001 -From: Balakrishnan Balasubramanian -Date: Tue, 25 Feb 2025 17:35:39 -0500 -Subject: [PATCH 2/2] Update anyhttp - -Run go mod tidy -Run go mod vendor -Update GetListener function - -diff --git a/go.mod b/go.mod -index 1dff4fe44..51f014ba4 100644 ---- a/go.mod -+++ b/go.mod -@@ -70,6 +70,7 @@ require ( - github.com/uptrace/bun/extra/bunotel v1.2.9 - github.com/wagslane/go-password-validator v0.3.0 - github.com/yuin/goldmark v1.7.8 -+ go.balki.me/anyhttp v0.5.0 - go.opentelemetry.io/otel v1.34.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.34.0 - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.34.0 -diff --git a/go.sum b/go.sum -index d1713cceb..18e258500 100644 ---- a/go.sum -+++ b/go.sum -@@ -619,6 +619,8 @@ github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic= - github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E= - gitlab.com/NyaaaWhatsUpDoc/sqlite v1.35.0-concurrency-workaround h1:rSPHdoNXzXyWQUUeMEy8pdOFn8lH7XqdBRTS9G+jdTg= - gitlab.com/NyaaaWhatsUpDoc/sqlite v1.35.0-concurrency-workaround/go.mod h1:9cr2sicr7jIaWTBKQmAxQLfBv9LL0su4ZTEV+utt3ic= -+go.balki.me/anyhttp v0.5.0 h1:uys0oRciBpZfwtxXAevScKy6amIQBXyDrcV0EtGF5zo= -+go.balki.me/anyhttp v0.5.0/go.mod h1:JhfekOIjgVODoVqUCficjpIgmB3wwlB7jhN0eN2EZ/s= - go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= - go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= - go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -diff --git a/internal/router/router.go b/internal/router/router.go -index 73bfc8e96..97ee65847 100644 ---- a/internal/router/router.go -+++ b/internal/router/router.go -@@ -164,9 +164,9 @@ func (r *Router) Start() error { - } - - // TLS handled by reverse proxy connecting using unix socket -- case strings.HasPrefix(bindAddr, "unix/"): -+ case strings.HasPrefix(bindAddr, "unix?"): - listen, err = func() (func() error, error) { -- _, listener, err := anyhttp.GetListener(bindAddr) -+ listener, _, _, err := anyhttp.GetListener(bindAddr) - if err != nil { - return nil, err - } -diff --git a/vendor/go.balki.me/anyhttp/README.md b/vendor/go.balki.me/anyhttp/README.md -index 7e9b11922..85bce6fc3 100644 ---- a/vendor/go.balki.me/anyhttp/README.md -+++ b/vendor/go.balki.me/anyhttp/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=false -+ -+| 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 -diff --git a/vendor/go.balki.me/anyhttp/anyhttp.go b/vendor/go.balki.me/anyhttp/anyhttp.go -index 5c0615442..8d316a78f 100644 ---- a/vendor/go.balki.me/anyhttp/anyhttp.go -+++ b/vendor/go.balki.me/anyhttp/anyhttp.go -@@ -2,25 +2,30 @@ - package anyhttp - - import ( -+ "context" - "errors" - "fmt" - "io/fs" - "net" - "net/http" -+ "net/url" - "os" - "strconv" - "strings" - "sync" - "syscall" -+ "time" -+ -+ "go.balki.me/anyhttp/idle" - ) - - // AddressType of the address passed - type AddressType string - - var ( -- // UnixSocket - address is a unix socket, e.g. unix//run/foo.sock -+ // UnixSocket - address is a unix socket, e.g. unix?path=/run/foo.sock - UnixSocket AddressType = "UnixSocket" -- // SystemdFD - address is a systemd fd, e.g. sysd/fdname/myapp.socket -+ // SystemdFD - address is a systemd fd, e.g. sysd?name=myapp.socket - SystemdFD AddressType = "SystemdFD" - // TCP - address is a TCP address, e.g. :1234 - TCP AddressType = "TCP" -@@ -97,6 +102,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 -@@ -196,69 +203,86 @@ 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 -- } +// GetListener is low level function for use with non-http servers. e.g. tcp, smtp +// Caller should handle idle timeout if needed +func GetListener(addr string) (net.Listener, AddressType, any /* cfg */, error) { - -- if strings.HasPrefix(addr, "sysd/fdidx/") { -- idx, err := strconv.Atoi(strings.TrimPrefix(addr, "sysd/fdidx/")) ++ + addrType, unixSocketConfig, sysdConfig, perr := parseAddress(addr) + if perr != nil { + return nil, Unknown, nil, perr + } + if unixSocketConfig != nil { + listener, err := unixSocketConfig.GetListener() - if err != nil { -- return Unknown, nil, fmt.Errorf("invalid fdidx, addr:%q err: %w", addr, err) ++ if err != nil { + return nil, Unknown, nil, 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) ++ } + return listener, addrType, unixSocketConfig, nil + } else if sysdConfig != nil { + listener, err := sysdConfig.GetListener() + if err != nil { + return nil, Unknown, nil, err - } ++ } + return listener, addrType, sysdConfig, nil - } -- - if addr == "" { - addr = ":http" - } -- -- l, err := net.Listen("tcp", addr) -- return TCP, l, err ++ } ++ if addr == "" { ++ addr = ":http" ++ } + listener, err := net.Listen("tcp", addr) + return listener, TCP, nil, 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) ++} ++ +type ServerCtx struct { + AddressType AddressType + Listener net.Listener @@ -1017,17 +920,9 @@ index 5c0615442..8d316a78f 100644 + +func (s *ServerCtx) Shutdown(ctx context.Context) error { + err := s.Server.Shutdown(ctx) - if err != nil { -- return addrType, nil, nil, err ++ if err != nil { + return err - } -- srv := &http.Server{Handler: h} -- done := make(chan error) -- go func() { -- done <- srv.Serve(listener) -- close(done) -- }() -- return addrType, srv, done, nil ++ } + return <-s.Done +} + @@ -1039,17 +934,15 @@ index 5c0615442..8d316a78f 100644 +// Serve creates and serves a HTTP server. +func Serve(addr string, h http.Handler) (*ServerCtx, error) { + return serve(addr, h, "", "") - } - - // 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) ++} ++ ++// ListenAndServe is the drop-in replacement for `http.ListenAndServe`. ++// Supports unix and systemd sockets in addition ++func ListenAndServe(addr string, h http.Handler) error { + ctx, err := Serve(addr, h) - if err != nil { - return err - } -- return <-done ++ if err != nil { ++ return err ++ } + return ctx.Wait() +} + @@ -1059,13 +952,14 @@ index 5c0615442..8d316a78f 100644 + return err + } + return ctx.Wait() - } - - // UnsetSystemdListenVars unsets the LISTEN* environment variables so they are not passed to any child processes -@@ -267,3 +291,150 @@ func UnsetSystemdListenVars() { - _ = os.Unsetenv("LISTEN_FDS") - _ = os.Unsetenv("LISTEN_FDNAMES") - } ++} ++ ++// UnsetSystemdListenVars unsets the LISTEN* environment variables so they are not passed to any child processes ++func UnsetSystemdListenVars() { ++ _ = os.Unsetenv("LISTEN_PID") ++ _ = os.Unsetenv("LISTEN_FDS") ++ _ = os.Unsetenv("LISTEN_FDNAMES") ++} + +func parseAddress(addr string) (addrType AddressType, usc *UnixSocketConfig, sysc *SysdConfig, err error) { + usc = nil @@ -1347,30 +1241,17 @@ index 000000000..ee3d81ff1 + return i.c +} diff --git a/vendor/modules.txt b/vendor/modules.txt -index dfb915c88..bd7da894d 100644 +index e053e6ce0..338ab8877 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt -@@ -466,8 +466,6 @@ github.com/microcosm-cc/bluemonday/css - # github.com/miekg/dns v1.1.63 - ## explicit; go 1.19 - github.com/miekg/dns --# github.com/minio/crc64nvme v1.0.0 --## explicit; go 1.22 - # github.com/minio/md5-simd v1.1.2 - ## explicit; go 1.14 - github.com/minio/md5-simd -@@ -961,9 +959,10 @@ github.com/yuin/goldmark/renderer +@@ -959,6 +959,10 @@ github.com/yuin/goldmark/renderer github.com/yuin/goldmark/renderer/html github.com/yuin/goldmark/text github.com/yuin/goldmark/util --# go.balki.me/anyhttp v0.3.0 +# go.balki.me/anyhttp v0.5.0 - ## explicit; go 1.20 - go.balki.me/anyhttp ++## explicit; go 1.20 ++go.balki.me/anyhttp +go.balki.me/anyhttp/idle # go.mongodb.org/mongo-driver v1.14.0 ## explicit; go 1.18 go.mongodb.org/mongo-driver/bson --- -2.48.1 -