mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-20 12:42:34 +02:00
refactor and organize code
This commit is contained in:
parent
1af6dd9cf8
commit
18d258aaa2
169 changed files with 1020 additions and 755 deletions
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/agent/pkg/env"
|
||||
"github.com/yusing/go-proxy/agent/pkg/server"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/pkg"
|
||||
|
@ -16,7 +16,7 @@ func main() {
|
|||
args := os.Args
|
||||
if len(args) > 1 && args[1] == "migrate" {
|
||||
if err := agent.MigrateFromOld(); err != nil {
|
||||
E.LogFatal("failed to migrate from old docker compose", err)
|
||||
gperr.LogFatal("failed to migrate from old docker compose", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -24,21 +24,21 @@ func main() {
|
|||
ca := &agent.PEMPair{}
|
||||
err := ca.Load(env.AgentCACert)
|
||||
if err != nil {
|
||||
E.LogFatal("init CA error", err)
|
||||
gperr.LogFatal("init CA error", err)
|
||||
}
|
||||
caCert, err := ca.ToTLSCert()
|
||||
if err != nil {
|
||||
E.LogFatal("init CA error", err)
|
||||
gperr.LogFatal("init CA error", err)
|
||||
}
|
||||
|
||||
srv := &agent.PEMPair{}
|
||||
srv.Load(env.AgentSSLCert)
|
||||
if err != nil {
|
||||
E.LogFatal("init SSL error", err)
|
||||
gperr.LogFatal("init SSL error", err)
|
||||
}
|
||||
srvCert, err := srv.ToTLSCert()
|
||||
if err != nil {
|
||||
E.LogFatal("init SSL error", err)
|
||||
gperr.LogFatal("init SSL error", err)
|
||||
}
|
||||
|
||||
logging.Info().Msgf("GoDoxy Agent version %s", pkg.GetVersion())
|
||||
|
|
|
@ -12,9 +12,9 @@ import (
|
|||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/yusing/go-proxy/agent/pkg/certs"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
gphttp "github.com/yusing/go-proxy/internal/net/http"
|
||||
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/pkg"
|
||||
|
@ -80,17 +80,17 @@ func checkVersion(a, b string) bool {
|
|||
return withoutBuildTime(a) == withoutBuildTime(b)
|
||||
}
|
||||
|
||||
func (cfg *AgentConfig) StartWithCerts(parent task.Parent, ca, crt, key []byte) E.Error {
|
||||
func (cfg *AgentConfig) StartWithCerts(parent task.Parent, ca, crt, key []byte) error {
|
||||
clientCert, err := tls.X509KeyPair(crt, key)
|
||||
if err != nil {
|
||||
return E.Wrap(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// create tls config
|
||||
caCertPool := x509.NewCertPool()
|
||||
ok := caCertPool.AppendCertsFromPEM(ca)
|
||||
if !ok {
|
||||
return E.New("invalid CA certificate")
|
||||
return gperr.New("invalid CA certificate")
|
||||
}
|
||||
|
||||
cfg.tlsConfig = &tls.Config{
|
||||
|
@ -108,17 +108,17 @@ func (cfg *AgentConfig) StartWithCerts(parent task.Parent, ca, crt, key []byte)
|
|||
// check agent version
|
||||
version, _, err := cfg.Fetch(ctx, EndpointVersion)
|
||||
if err != nil {
|
||||
return E.Wrap(err)
|
||||
return err
|
||||
}
|
||||
|
||||
if !checkVersion(string(version), pkg.GetVersion()) {
|
||||
return E.Errorf("agent version mismatch: server: %s, agent: %s", pkg.GetVersion(), string(version))
|
||||
return gperr.Errorf("agent version mismatch: server: %s, agent: %s", pkg.GetVersion(), string(version))
|
||||
}
|
||||
|
||||
// get agent name
|
||||
name, _, err := cfg.Fetch(ctx, EndpointName)
|
||||
if err != nil {
|
||||
return E.Wrap(err)
|
||||
return err
|
||||
}
|
||||
|
||||
cfg.name = string(name)
|
||||
|
@ -128,18 +128,18 @@ func (cfg *AgentConfig) StartWithCerts(parent task.Parent, ca, crt, key []byte)
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cfg *AgentConfig) Start(parent task.Parent) E.Error {
|
||||
func (cfg *AgentConfig) Start(parent task.Parent) error {
|
||||
certData, err := os.ReadFile(certs.AgentCertsFilename(cfg.Addr))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return E.Errorf("agents certs not found, did you run `godoxy new-agent %s ...`?", cfg.Addr)
|
||||
return gperr.Errorf("agents certs not found, did you run `godoxy new-agent %s ...`?", cfg.Addr)
|
||||
}
|
||||
return E.Wrap(err)
|
||||
return gperr.Wrap(err)
|
||||
}
|
||||
|
||||
ca, crt, key, err := certs.ExtractCert(certData)
|
||||
if err != nil {
|
||||
return E.Wrap(err)
|
||||
return gperr.Wrap(err)
|
||||
}
|
||||
|
||||
return cfg.StartWithCerts(parent, ca, crt, key)
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
|
||||
_ "embed"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
@ -110,11 +110,11 @@ func MigrateFromOld() error {
|
|||
composeConfig.SSLCert = agentCert.String()
|
||||
composeTemplate, err := composeConfig.Generate()
|
||||
if err != nil {
|
||||
return E.Wrap(err, "failed to generate new docker compose")
|
||||
return gperr.Wrap(err, "failed to generate new docker compose")
|
||||
}
|
||||
|
||||
if err := os.WriteFile("/app/compose.yml", []byte(composeTemplate), 0600); err != nil {
|
||||
return E.Wrap(err, "failed to write new docker compose")
|
||||
return gperr.Wrap(err, "failed to write new docker compose")
|
||||
}
|
||||
|
||||
logging.Info().Msg("Migrated from old docker compose:")
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
|
||||
apiUtils "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health/monitor"
|
||||
|
@ -72,5 +72,5 @@ func CheckHealth(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
apiUtils.RespondJSON(w, r, result)
|
||||
gphttp.RespondJSON(w, r, result)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/docker"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/net/http/reverseproxy"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
||||
"github.com/yusing/go-proxy/internal/net/types"
|
||||
)
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
agentproxy "github.com/yusing/go-proxy/agent/pkg/agentproxy"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agentproxy"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
gphttp "github.com/yusing/go-proxy/internal/net/http"
|
||||
"github.com/yusing/go-proxy/internal/net/http/reverseproxy"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
||||
"github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
|
|
@ -14,7 +14,7 @@ import (
|
|||
"github.com/yusing/go-proxy/agent/pkg/env"
|
||||
"github.com/yusing/go-proxy/agent/pkg/handler"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/net/http/server"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/server"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
)
|
||||
|
||||
|
|
10
cmd/main.go
10
cmd/main.go
|
@ -11,10 +11,10 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/api/v1/query"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/config"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/net/http/middleware"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/middleware"
|
||||
"github.com/yusing/go-proxy/internal/route/routes/routequery"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/pkg"
|
||||
|
@ -32,7 +32,7 @@ func main() {
|
|||
return
|
||||
case common.CommandReload:
|
||||
if err := query.ReloadServer(); err != nil {
|
||||
E.LogFatal("server reload error", err)
|
||||
gperr.LogFatal("server reload error", err)
|
||||
}
|
||||
rawLogger.Println("ok")
|
||||
return
|
||||
|
@ -88,9 +88,9 @@ func main() {
|
|||
middleware.LoadComposeFiles()
|
||||
|
||||
var cfg *config.Config
|
||||
var err E.Error
|
||||
var err gperr.Error
|
||||
if cfg, err = config.Load(); err != nil {
|
||||
E.LogWarn("errors in config", err)
|
||||
gperr.LogWarn("errors in config", err)
|
||||
}
|
||||
|
||||
switch args.Command {
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/logging/memlogger"
|
||||
"github.com/yusing/go-proxy/internal/metrics/uptime"
|
||||
"github.com/yusing/go-proxy/internal/net/http/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
|
|
|
@ -6,18 +6,19 @@ import (
|
|||
|
||||
"github.com/coder/websocket"
|
||||
"github.com/coder/websocket/wsjson"
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/net/http/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/gpwebsocket"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
)
|
||||
|
||||
func ListAgents(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||
if httpheaders.IsWebsocket(r.Header) {
|
||||
U.PeriodicWS(w, r, 10*time.Second, func(conn *websocket.Conn) error {
|
||||
gpwebsocket.Periodic(w, r, 10*time.Second, func(conn *websocket.Conn) error {
|
||||
wsjson.Write(r.Context(), conn, cfg.ListAgents())
|
||||
return nil
|
||||
})
|
||||
} else {
|
||||
U.RespondJSON(w, r, cfg.ListAgents())
|
||||
gphttp.RespondJSON(w, r, cfg.ListAgents())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@ package auth
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
)
|
||||
|
||||
var defaultAuth Provider
|
||||
|
@ -44,7 +44,7 @@ func RequireAuth(next http.HandlerFunc) http.HandlerFunc {
|
|||
if IsEnabled() {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := defaultAuth.CheckToken(r); err != nil {
|
||||
U.RespondError(w, err, http.StatusUnauthorized)
|
||||
gphttp.ClientError(w, err, http.StatusUnauthorized)
|
||||
} else {
|
||||
next(w, r)
|
||||
}
|
||||
|
|
|
@ -12,9 +12,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
CE "github.com/yusing/go-proxy/internal/utils"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
"golang.org/x/oauth2"
|
||||
|
@ -154,7 +153,7 @@ func generateState() (string, error) {
|
|||
func (auth *OIDCProvider) RedirectLoginPage(w http.ResponseWriter, r *http.Request) {
|
||||
state, err := generateState()
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err, http.StatusInternalServerError)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
|
@ -171,7 +170,7 @@ func (auth *OIDCProvider) RedirectLoginPage(w http.ResponseWriter, r *http.Reque
|
|||
if auth.isMiddleware {
|
||||
u, err := r.URL.Parse(redirURL)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err, http.StatusInternalServerError)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
q := u.Query()
|
||||
|
@ -201,31 +200,31 @@ func (auth *OIDCProvider) LoginCallbackHandler(w http.ResponseWriter, r *http.Re
|
|||
|
||||
state, err := r.Cookie(CookieOauthState)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, E.New("missing state cookie"), http.StatusBadRequest)
|
||||
gphttp.BadRequest(w, "missing state cookie")
|
||||
return
|
||||
}
|
||||
|
||||
query := r.URL.Query()
|
||||
if query.Get("state") != state.Value {
|
||||
U.HandleErr(w, r, E.New("invalid oauth state"), http.StatusBadRequest)
|
||||
gphttp.BadRequest(w, "invalid oauth state")
|
||||
return
|
||||
}
|
||||
|
||||
oauth2Token, err := auth.exchange(r)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, fmt.Errorf("failed to exchange token: %w", err), http.StatusInternalServerError)
|
||||
gphttp.ServerError(w, r, fmt.Errorf("failed to exchange token: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
rawIDToken, ok := oauth2Token.Extra("id_token").(string)
|
||||
if !ok {
|
||||
U.HandleErr(w, r, E.New("missing id_token"), http.StatusInternalServerError)
|
||||
gphttp.BadRequest(w, "missing id_token")
|
||||
return
|
||||
}
|
||||
|
||||
idToken, err := auth.oidcVerifier.Verify(r.Context(), rawIDToken)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, fmt.Errorf("failed to verify ID token: %w", err), http.StatusInternalServerError)
|
||||
gphttp.ServerError(w, r, fmt.Errorf("failed to verify ID token: %w", err))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -243,7 +242,7 @@ func (auth *OIDCProvider) LogoutCallbackHandler(w http.ResponseWriter, r *http.R
|
|||
|
||||
token, err := r.Cookie(auth.TokenCookieName())
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, E.New("missing token cookie"), http.StatusBadRequest)
|
||||
gphttp.BadRequest(w, "missing token cookie")
|
||||
return
|
||||
}
|
||||
clearTokenCookie(w, r, auth.TokenCookieName())
|
||||
|
@ -258,12 +257,12 @@ func (auth *OIDCProvider) LogoutCallbackHandler(w http.ResponseWriter, r *http.R
|
|||
func (auth *OIDCProvider) handleTestCallback(w http.ResponseWriter, r *http.Request) {
|
||||
state, err := r.Cookie(CookieOauthState)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, E.New("missing state cookie"), http.StatusBadRequest)
|
||||
gphttp.BadRequest(w, "missing state cookie")
|
||||
return
|
||||
}
|
||||
|
||||
if r.URL.Query().Get("state") != state.Value {
|
||||
U.HandleErr(w, r, E.New("invalid oauth state"), http.StatusBadRequest)
|
||||
gphttp.BadRequest(w, "invalid oauth state")
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -7,16 +7,16 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidUsername = E.New("invalid username")
|
||||
ErrInvalidPassword = E.New("invalid password")
|
||||
ErrInvalidUsername = gperr.New("invalid username")
|
||||
ErrInvalidPassword = gperr.New("invalid password")
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -94,7 +94,7 @@ func (auth *UserPassAuth) CheckToken(r *http.Request) error {
|
|||
case claims.Username != auth.username:
|
||||
return ErrUserNotAllowed.Subject(claims.Username)
|
||||
case claims.ExpiresAt.Before(time.Now()):
|
||||
return E.Errorf("token expired on %s", strutils.FormatTime(claims.ExpiresAt.Time))
|
||||
return gperr.Errorf("token expired on %s", strutils.FormatTime(claims.ExpiresAt.Time))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -111,17 +111,16 @@ func (auth *UserPassAuth) LoginCallbackHandler(w http.ResponseWriter, r *http.Re
|
|||
}
|
||||
err := json.NewDecoder(r.Body).Decode(&creds)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err, http.StatusBadRequest)
|
||||
gphttp.Unauthorized(w, "invalid credentials")
|
||||
return
|
||||
}
|
||||
if err := auth.validatePassword(creds.User, creds.Pass); err != nil {
|
||||
U.LogError(r).Err(err).Msg("auth: invalid credentials")
|
||||
U.RespondError(w, E.New("invalid credentials"), http.StatusUnauthorized)
|
||||
gphttp.Unauthorized(w, "invalid credentials")
|
||||
return
|
||||
}
|
||||
token, err := auth.NewToken()
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err, http.StatusInternalServerError)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
setTokenCookie(w, r, auth.TokenCookieName(), token, auth.tokenTTL)
|
||||
|
|
|
@ -5,14 +5,14 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrMissingToken = E.New("missing token")
|
||||
ErrInvalidToken = E.New("invalid token")
|
||||
ErrUserNotAllowed = E.New("user not allowed")
|
||||
ErrMissingToken = gperr.New("missing token")
|
||||
ErrInvalidToken = gperr.New("invalid token")
|
||||
ErrUserNotAllowed = gperr.New("user not allowed")
|
||||
)
|
||||
|
||||
// cookieFQDN returns the fully qualified domain name of the request host
|
||||
|
|
|
@ -13,10 +13,10 @@ import (
|
|||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/vincent-petithory/dataurl"
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
gphttp "github.com/yusing/go-proxy/internal/net/http"
|
||||
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
route "github.com/yusing/go-proxy/internal/route/types"
|
||||
)
|
||||
|
@ -53,11 +53,11 @@ func (res *fetchResult) ContentType() string {
|
|||
func GetFavIcon(w http.ResponseWriter, req *http.Request) {
|
||||
url, alias := req.FormValue("url"), req.FormValue("alias")
|
||||
if url == "" && alias == "" {
|
||||
U.RespondError(w, U.ErrMissingKey("url or alias"), http.StatusBadRequest)
|
||||
gphttp.ClientError(w, gphttp.ErrMissingKey("url or alias"), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if url != "" && alias != "" {
|
||||
U.RespondError(w, U.ErrInvalidKey("url and alias are mutually exclusive"), http.StatusBadRequest)
|
||||
gphttp.ClientError(w, gperr.New("url and alias are mutually exclusive"), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ func GetFavIcon(w http.ResponseWriter, req *http.Request) {
|
|||
if url != "" {
|
||||
var iconURL homepage.IconURL
|
||||
if err := iconURL.Parse(url); err != nil {
|
||||
U.RespondError(w, err, http.StatusBadRequest)
|
||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
fetchResult := getFavIconFromURL(&iconURL)
|
||||
|
@ -74,14 +74,14 @@ func GetFavIcon(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", fetchResult.ContentType())
|
||||
U.WriteBody(w, fetchResult.icon)
|
||||
gphttp.WriteBody(w, fetchResult.icon)
|
||||
return
|
||||
}
|
||||
|
||||
// try with route.Homepage.Icon
|
||||
r, ok := routes.GetHTTPRoute(alias)
|
||||
if !ok {
|
||||
U.RespondError(w, errors.New("no such route"), http.StatusNotFound)
|
||||
gphttp.ClientError(w, errors.New("no such route"), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ func GetFavIcon(w http.ResponseWriter, req *http.Request) {
|
|||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", result.ContentType())
|
||||
U.WriteBody(w, result.icon)
|
||||
gphttp.WriteBody(w, result.icon)
|
||||
}
|
||||
|
||||
func getFavIconFromURL(iconURL *homepage.IconURL) *fetchResult {
|
||||
|
@ -125,7 +125,7 @@ func fetchIconAbsolute(url string) *fetchResult {
|
|||
return result
|
||||
}
|
||||
|
||||
resp, err := U.Get(url)
|
||||
resp, err := gphttp.Get(url)
|
||||
if err != nil || resp.StatusCode != http.StatusOK {
|
||||
if err == nil {
|
||||
err = errors.New(resp.Status)
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
"path"
|
||||
"strings"
|
||||
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/net/http/middleware"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/middleware"
|
||||
"github.com/yusing/go-proxy/internal/route/provider"
|
||||
)
|
||||
|
||||
|
@ -51,12 +51,12 @@ func (t FileType) GetPath(filename string) string {
|
|||
func getArgs(r *http.Request) (fileType FileType, filename string, err error) {
|
||||
fileType = FileType(r.PathValue("type"))
|
||||
if !fileType.IsValid() {
|
||||
err = U.ErrInvalidKey("type")
|
||||
err = gphttp.ErrInvalidKey("type")
|
||||
return
|
||||
}
|
||||
filename = r.PathValue("filename")
|
||||
if filename == "" {
|
||||
err = U.ErrMissingKey("filename")
|
||||
err = gphttp.ErrMissingKey("filename")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -64,23 +64,23 @@ func getArgs(r *http.Request) (fileType FileType, filename string, err error) {
|
|||
func GetFileContent(w http.ResponseWriter, r *http.Request) {
|
||||
fileType, filename, err := getArgs(r)
|
||||
if err != nil {
|
||||
U.RespondError(w, err, http.StatusBadRequest)
|
||||
gphttp.BadRequest(w, err.Error())
|
||||
return
|
||||
}
|
||||
content, err := os.ReadFile(fileType.GetPath(filename))
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
U.WriteBody(w, content)
|
||||
gphttp.WriteBody(w, content)
|
||||
}
|
||||
|
||||
func validateFile(fileType FileType, content []byte) error {
|
||||
func validateFile(fileType FileType, content []byte) gperr.Error {
|
||||
switch fileType {
|
||||
case FileTypeConfig:
|
||||
return config.Validate(content)
|
||||
case FileTypeMiddleware:
|
||||
errs := E.NewBuilder("middleware errors")
|
||||
errs := gperr.NewBuilder("middleware errors")
|
||||
middleware.BuildMiddlewaresFromYAML("", content, errs)
|
||||
return errs.Error()
|
||||
}
|
||||
|
@ -90,18 +90,17 @@ func validateFile(fileType FileType, content []byte) error {
|
|||
func ValidateFile(w http.ResponseWriter, r *http.Request) {
|
||||
fileType := FileType(r.PathValue("type"))
|
||||
if !fileType.IsValid() {
|
||||
U.RespondError(w, U.ErrInvalidKey("type"), http.StatusBadRequest)
|
||||
gphttp.BadRequest(w, "invalid file type")
|
||||
return
|
||||
}
|
||||
content, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
r.Body.Close()
|
||||
err = validateFile(fileType, content)
|
||||
if err != nil {
|
||||
U.RespondError(w, err, http.StatusBadRequest)
|
||||
if valErr := validateFile(fileType, content); valErr != nil {
|
||||
gphttp.JSONError(w, valErr, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
@ -110,23 +109,23 @@ func ValidateFile(w http.ResponseWriter, r *http.Request) {
|
|||
func SetFileContent(w http.ResponseWriter, r *http.Request) {
|
||||
fileType, filename, err := getArgs(r)
|
||||
if err != nil {
|
||||
U.RespondError(w, err, http.StatusBadRequest)
|
||||
gphttp.BadRequest(w, err.Error())
|
||||
return
|
||||
}
|
||||
content, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
if valErr := validateFile(fileType, content); valErr != nil {
|
||||
U.RespondError(w, valErr, http.StatusBadRequest)
|
||||
gphttp.JSONError(w, valErr, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err = os.WriteFile(fileType.GetPath(filename), content, 0o644)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
|
|
@ -6,17 +6,18 @@ import (
|
|||
|
||||
"github.com/coder/websocket"
|
||||
"github.com/coder/websocket/wsjson"
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/net/http/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/gpwebsocket"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/route/routes/routequery"
|
||||
)
|
||||
|
||||
func Health(w http.ResponseWriter, r *http.Request) {
|
||||
if httpheaders.IsWebsocket(r.Header) {
|
||||
U.PeriodicWS(w, r, 1*time.Second, func(conn *websocket.Conn) error {
|
||||
gpwebsocket.Periodic(w, r, 1*time.Second, func(conn *websocket.Conn) error {
|
||||
return wsjson.Write(r.Context(), conn, routequery.HealthMap())
|
||||
})
|
||||
} else {
|
||||
U.RespondJSON(w, r, routequery.HealthMap())
|
||||
gphttp.RespondJSON(w, r, routequery.HealthMap())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -37,13 +37,13 @@ type (
|
|||
func SetHomePageOverrides(w http.ResponseWriter, r *http.Request) {
|
||||
what := r.FormValue("what")
|
||||
if what == "" {
|
||||
http.Error(w, "missing what or which", http.StatusBadRequest)
|
||||
gphttp.BadRequest(w, "missing what or which")
|
||||
return
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
utils.RespondError(w, err, http.StatusBadRequest)
|
||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
r.Body.Close()
|
||||
|
@ -53,21 +53,21 @@ func SetHomePageOverrides(w http.ResponseWriter, r *http.Request) {
|
|||
case HomepageOverrideItem:
|
||||
var params HomepageOverrideItemParams
|
||||
if err := json.Unmarshal(data, ¶ms); err != nil {
|
||||
utils.RespondError(w, err, http.StatusBadRequest)
|
||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
overrides.OverrideItem(params.Which, ¶ms.Value)
|
||||
case HomepageOverrideItemsBatch:
|
||||
var params HomepageOverrideItemsBatchParams
|
||||
if err := json.Unmarshal(data, ¶ms); err != nil {
|
||||
utils.RespondError(w, err, http.StatusBadRequest)
|
||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
overrides.OverrideItems(params.Value)
|
||||
case HomepageOverrideItemVisible: // POST /v1/item_visible [a,b,c], false => hide a, b, c
|
||||
var params HomepageOverrideItemVisibleParams
|
||||
if err := json.Unmarshal(data, ¶ms); err != nil {
|
||||
utils.RespondError(w, err, http.StatusBadRequest)
|
||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if params.Value {
|
||||
|
@ -78,7 +78,7 @@ func SetHomePageOverrides(w http.ResponseWriter, r *http.Request) {
|
|||
case HomepageOverrideCategoryOrder:
|
||||
var params HomepageOverrideCategoryOrderParams
|
||||
if err := json.Unmarshal(data, ¶ms); err != nil {
|
||||
utils.RespondError(w, err, http.StatusBadRequest)
|
||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
overrides.SetCategoryOrder(params.Which, params.Value)
|
||||
|
|
|
@ -3,9 +3,9 @@ package v1
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
. "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
)
|
||||
|
||||
func Index(w http.ResponseWriter, r *http.Request) {
|
||||
WriteBody(w, []byte("API ready"))
|
||||
gphttp.WriteBody(w, []byte("API ready"))
|
||||
}
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
package v1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/yusing/go-proxy/internal"
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/net/http/middleware"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/middleware"
|
||||
"github.com/yusing/go-proxy/internal/route/routes/routequery"
|
||||
route "github.com/yusing/go-proxy/internal/route/types"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
|
@ -41,26 +42,25 @@ func List(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
|||
case ListRoute:
|
||||
if route := listRoute(which); route == nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
} else {
|
||||
U.RespondJSON(w, r, route)
|
||||
gphttp.RespondJSON(w, r, route)
|
||||
}
|
||||
case ListRoutes:
|
||||
U.RespondJSON(w, r, routequery.RoutesByAlias(route.RouteType(r.FormValue("type"))))
|
||||
gphttp.RespondJSON(w, r, routequery.RoutesByAlias(route.RouteType(r.FormValue("type"))))
|
||||
case ListFiles:
|
||||
listFiles(w, r)
|
||||
case ListMiddlewares:
|
||||
U.RespondJSON(w, r, middleware.All())
|
||||
gphttp.RespondJSON(w, r, middleware.All())
|
||||
case ListMiddlewareTraces:
|
||||
U.RespondJSON(w, r, middleware.GetAllTrace())
|
||||
gphttp.RespondJSON(w, r, middleware.GetAllTrace())
|
||||
case ListMatchDomains:
|
||||
U.RespondJSON(w, r, cfg.Value().MatchDomains)
|
||||
gphttp.RespondJSON(w, r, cfg.Value().MatchDomains)
|
||||
case ListHomepageConfig:
|
||||
U.RespondJSON(w, r, routequery.HomepageConfig(cfg.Value().Homepage.UseDefaultCategories, r.FormValue("category"), r.FormValue("provider")))
|
||||
gphttp.RespondJSON(w, r, routequery.HomepageConfig(cfg.Value().Homepage.UseDefaultCategories, r.FormValue("category"), r.FormValue("provider")))
|
||||
case ListRouteProviders:
|
||||
U.RespondJSON(w, r, cfg.RouteProviderList())
|
||||
gphttp.RespondJSON(w, r, cfg.RouteProviderList())
|
||||
case ListHomepageCategories:
|
||||
U.RespondJSON(w, r, routequery.HomepageCategories())
|
||||
gphttp.RespondJSON(w, r, routequery.HomepageCategories())
|
||||
case ListIcons:
|
||||
limit, err := strconv.Atoi(r.FormValue("limit"))
|
||||
if err != nil {
|
||||
|
@ -68,17 +68,17 @@ func List(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
icons, err := internal.SearchIcons(r.FormValue("keyword"), limit)
|
||||
if err != nil {
|
||||
U.RespondError(w, err)
|
||||
gphttp.ClientError(w, err)
|
||||
return
|
||||
}
|
||||
if icons == nil {
|
||||
icons = []string{}
|
||||
}
|
||||
U.RespondJSON(w, r, icons)
|
||||
gphttp.RespondJSON(w, r, icons)
|
||||
case ListTasks:
|
||||
U.RespondJSON(w, r, task.DebugTaskList())
|
||||
gphttp.RespondJSON(w, r, task.DebugTaskList())
|
||||
default:
|
||||
U.HandleErr(w, r, U.ErrInvalidKey("what"), http.StatusBadRequest)
|
||||
gphttp.BadRequest(w, fmt.Sprintf("invalid what: %s", what))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ func listRoute(which string) any {
|
|||
func listFiles(w http.ResponseWriter, r *http.Request) {
|
||||
files, err := utils.ListFiles(common.ConfigBasePath, 0, true)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
resp := map[FileType][]string{
|
||||
|
@ -116,12 +116,12 @@ func listFiles(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
mids, err := utils.ListFiles(common.MiddlewareComposeBasePath, 0, true)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
for _, mid := range mids {
|
||||
mid = strings.TrimPrefix(mid, common.MiddlewareComposeBasePath+"/")
|
||||
resp[FileTypeMiddleware] = append(resp[FileTypeMiddleware], mid)
|
||||
}
|
||||
U.RespondJSON(w, r, resp)
|
||||
gphttp.RespondJSON(w, r, resp)
|
||||
}
|
||||
|
|
|
@ -12,8 +12,9 @@ import (
|
|||
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/agent/pkg/certs"
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
|
@ -21,27 +22,27 @@ func NewAgent(w http.ResponseWriter, r *http.Request) {
|
|||
q := r.URL.Query()
|
||||
name := q.Get("name")
|
||||
if name == "" {
|
||||
U.RespondError(w, U.ErrMissingKey("name"))
|
||||
gphttp.ClientError(w, gphttp.ErrMissingKey("name"))
|
||||
return
|
||||
}
|
||||
host := q.Get("host")
|
||||
if host == "" {
|
||||
U.RespondError(w, U.ErrMissingKey("host"))
|
||||
gphttp.ClientError(w, gphttp.ErrMissingKey("host"))
|
||||
return
|
||||
}
|
||||
portStr := q.Get("port")
|
||||
if portStr == "" {
|
||||
U.RespondError(w, U.ErrMissingKey("port"))
|
||||
gphttp.ClientError(w, gphttp.ErrMissingKey("port"))
|
||||
return
|
||||
}
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil || port < 1 || port > 65535 {
|
||||
U.RespondError(w, U.ErrInvalidKey("port"))
|
||||
gphttp.ClientError(w, gphttp.ErrInvalidKey("port"))
|
||||
return
|
||||
}
|
||||
hostport := fmt.Sprintf("%s:%d", host, port)
|
||||
if _, ok := config.GetInstance().GetAgent(hostport); ok {
|
||||
U.RespondError(w, U.ErrAlreadyExists("agent", hostport), http.StatusConflict)
|
||||
gphttp.ClientError(w, gphttp.ErrAlreadyExists("agent", hostport), http.StatusConflict)
|
||||
return
|
||||
}
|
||||
t := q.Get("type")
|
||||
|
@ -49,13 +50,13 @@ func NewAgent(w http.ResponseWriter, r *http.Request) {
|
|||
case "docker":
|
||||
break
|
||||
case "system":
|
||||
U.RespondError(w, U.Errorf("system agent is not supported yet"), http.StatusNotImplemented)
|
||||
gphttp.ClientError(w, gperr.Errorf("system agent is not supported yet"), http.StatusNotImplemented)
|
||||
return
|
||||
case "":
|
||||
U.RespondError(w, U.ErrMissingKey("type"))
|
||||
gphttp.ClientError(w, gphttp.ErrMissingKey("type"))
|
||||
return
|
||||
default:
|
||||
U.RespondError(w, U.ErrInvalidKey("type"))
|
||||
gphttp.ClientError(w, gphttp.ErrInvalidKey("type"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -69,7 +70,7 @@ func NewAgent(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
ca, srv, client, err := agent.NewAgent()
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -83,11 +84,11 @@ func NewAgent(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
template, err := cfg.Generate()
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
U.RespondJSON(w, r, map[string]any{
|
||||
gphttp.RespondJSON(w, r, map[string]any{
|
||||
"compose": template,
|
||||
"ca": ca,
|
||||
"client": client,
|
||||
|
@ -98,7 +99,7 @@ func AddAgent(w http.ResponseWriter, r *http.Request) {
|
|||
defer r.Body.Close()
|
||||
clientPEMData, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -109,24 +110,24 @@ func AddAgent(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if err := json.Unmarshal(clientPEMData, &data); err != nil {
|
||||
U.RespondError(w, err, http.StatusBadRequest)
|
||||
gphttp.ClientError(w, err, http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
nRoutesAdded, err := config.GetInstance().AddAgent(data.Host, data.CA, data.Client)
|
||||
if err != nil {
|
||||
U.RespondError(w, err)
|
||||
gphttp.ClientError(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
zip, err := certs.ZipCert(data.CA.Cert, data.Client.Cert, data.Client.Key)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.WriteFile(certs.AgentCertsFilename(data.Host), zip, 0600); err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -7,20 +7,20 @@ import (
|
|||
"net/http"
|
||||
|
||||
v1 "github.com/yusing/go-proxy/internal/api/v1"
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/net/http/middleware"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/middleware"
|
||||
)
|
||||
|
||||
func ReloadServer() E.Error {
|
||||
resp, err := U.Post(common.APIHTTPURL+"/v1/reload", "", nil)
|
||||
func ReloadServer() gperr.Error {
|
||||
resp, err := gphttp.Post(common.APIHTTPURL+"/v1/reload", "", nil)
|
||||
if err != nil {
|
||||
return E.From(err)
|
||||
return gperr.Wrap(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
failure := E.Errorf("server reload status %v", resp.StatusCode)
|
||||
failure := gperr.Errorf("server reload status %v", resp.StatusCode)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return failure.With(err)
|
||||
|
@ -31,34 +31,34 @@ func ReloadServer() E.Error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func List[T any](what string) (_ T, outErr E.Error) {
|
||||
resp, err := U.Get(fmt.Sprintf("%s/v1/list/%s", common.APIHTTPURL, what))
|
||||
func List[T any](what string) (_ T, outErr gperr.Error) {
|
||||
resp, err := gphttp.Get(fmt.Sprintf("%s/v1/list/%s", common.APIHTTPURL, what))
|
||||
if err != nil {
|
||||
outErr = E.From(err)
|
||||
outErr = gperr.Wrap(err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
outErr = E.Errorf("list %s: failed, status %v", what, resp.StatusCode)
|
||||
outErr = gperr.Errorf("list %s: failed, status %v", what, resp.StatusCode)
|
||||
return
|
||||
}
|
||||
var res T
|
||||
err = json.NewDecoder(resp.Body).Decode(&res)
|
||||
if err != nil {
|
||||
outErr = E.From(err)
|
||||
outErr = gperr.Wrap(err)
|
||||
return
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func ListRoutes() (map[string]map[string]any, E.Error) {
|
||||
func ListRoutes() (map[string]map[string]any, gperr.Error) {
|
||||
return List[map[string]map[string]any](v1.ListRoutes)
|
||||
}
|
||||
|
||||
func ListMiddlewareTraces() (middleware.Traces, E.Error) {
|
||||
func ListMiddlewareTraces() (middleware.Traces, gperr.Error) {
|
||||
return List[middleware.Traces](v1.ListMiddlewareTraces)
|
||||
}
|
||||
|
||||
func DebugListTasks() (map[string]any, E.Error) {
|
||||
func DebugListTasks() (map[string]any, gperr.Error) {
|
||||
return List[map[string]any](v1.ListTasks)
|
||||
}
|
||||
|
|
|
@ -3,14 +3,14 @@ package v1
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
)
|
||||
|
||||
func Reload(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||
if err := cfg.Reload(); err != nil {
|
||||
U.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
U.WriteBody(w, []byte("OK"))
|
||||
gphttp.WriteBody(w, []byte("OK"))
|
||||
}
|
||||
|
|
|
@ -6,19 +6,20 @@ import (
|
|||
|
||||
"github.com/coder/websocket"
|
||||
"github.com/coder/websocket/wsjson"
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/net/http/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/gpwebsocket"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
func Stats(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||
if httpheaders.IsWebsocket(r.Header) {
|
||||
U.PeriodicWS(w, r, 1*time.Second, func(conn *websocket.Conn) error {
|
||||
gpwebsocket.Periodic(w, r, 1*time.Second, func(conn *websocket.Conn) error {
|
||||
return wsjson.Write(r.Context(), conn, getStats(cfg))
|
||||
})
|
||||
} else {
|
||||
U.RespondJSON(w, r, getStats(cfg))
|
||||
gphttp.RespondJSON(w, r, getStats(cfg))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,12 +4,12 @@ import (
|
|||
"net/http"
|
||||
|
||||
agentPkg "github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
U "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/metrics/systeminfo"
|
||||
"github.com/yusing/go-proxy/internal/net/http/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/http/reverseproxy"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
||||
)
|
||||
|
||||
func SystemInfo(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -23,7 +23,7 @@ func SystemInfo(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Reques
|
|||
|
||||
agent, ok := cfg.GetAgent(agentAddr)
|
||||
if !ok {
|
||||
U.HandleErr(w, r, U.ErrInvalidKey("agent_addr"), http.StatusNotFound)
|
||||
gphttp.NotFound(w, "agent_addr")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -31,20 +31,20 @@ func SystemInfo(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Reques
|
|||
if !isWS {
|
||||
respData, status, err := agent.Forward(r, agentPkg.EndpointSystemInfo)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, E.Wrap(err, "failed to forward request to agent"))
|
||||
gphttp.ServerError(w, r, gperr.Wrap(err, "failed to forward request to agent"))
|
||||
return
|
||||
}
|
||||
if status != http.StatusOK {
|
||||
http.Error(w, string(respData), status)
|
||||
return
|
||||
}
|
||||
U.WriteBody(w, respData)
|
||||
gphttp.WriteBody(w, respData)
|
||||
} else {
|
||||
rp := reverseproxy.NewReverseProxy("agent", agentPkg.AgentURL, agent.Transport())
|
||||
header := r.Header.Clone()
|
||||
r, err := http.NewRequestWithContext(r.Context(), r.Method, agentPkg.EndpointSystemInfo+"?"+query.Encode(), nil)
|
||||
if err != nil {
|
||||
U.HandleErr(w, r, E.Wrap(err, "failed to create request"))
|
||||
gphttp.ServerError(w, r, gperr.Wrap(err, "failed to create request"))
|
||||
return
|
||||
}
|
||||
r.Header = header
|
||||
|
|
|
@ -1,66 +0,0 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/http"
|
||||
"syscall"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/net/http/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils/ansi"
|
||||
)
|
||||
|
||||
// HandleErr logs the error and returns an error code to the client.
|
||||
// If code is specified, it will be used as the HTTP status code; otherwise,
|
||||
// http.StatusInternalServerError is used.
|
||||
//
|
||||
// The error is only logged but not returned to the client.
|
||||
func HandleErr(w http.ResponseWriter, r *http.Request, err error, code ...int) {
|
||||
switch {
|
||||
case err == nil,
|
||||
errors.Is(err, context.Canceled),
|
||||
errors.Is(err, syscall.EPIPE),
|
||||
errors.Is(err, syscall.ECONNRESET):
|
||||
return
|
||||
}
|
||||
LogError(r).Msg(err.Error())
|
||||
if httpheaders.IsWebsocket(r.Header) {
|
||||
return
|
||||
}
|
||||
if len(code) == 0 {
|
||||
code = []int{http.StatusInternalServerError}
|
||||
}
|
||||
http.Error(w, http.StatusText(code[0]), code[0])
|
||||
}
|
||||
|
||||
// RespondError returns error details to the client.
|
||||
// If code is specified, it will be used as the HTTP status code; otherwise,
|
||||
// http.StatusBadRequest is used.
|
||||
func RespondError(w http.ResponseWriter, err error, code ...int) {
|
||||
if len(code) == 0 {
|
||||
code = []int{http.StatusBadRequest}
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
http.Error(w, ansi.StripANSI(err.Error()), code[0])
|
||||
}
|
||||
|
||||
func Errorf(format string, args ...any) error {
|
||||
return E.Errorf(format, args...)
|
||||
}
|
||||
|
||||
func ErrMissingKey(k string) error {
|
||||
return E.New(k + " is required")
|
||||
}
|
||||
|
||||
func ErrInvalidKey(k string) error {
|
||||
return E.New(k + " is invalid")
|
||||
}
|
||||
|
||||
func ErrAlreadyExists(k, v string) error {
|
||||
return E.Errorf("%s %q already exists", k, v)
|
||||
}
|
||||
|
||||
func ErrNotFound(k, v string) error {
|
||||
return E.Errorf("%s %q not found", k, v)
|
||||
}
|
|
@ -3,10 +3,10 @@ package v1
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
. "github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/pkg"
|
||||
)
|
||||
|
||||
func GetVersion(w http.ResponseWriter, r *http.Request) {
|
||||
WriteBody(w, []byte(pkg.GetVersion()))
|
||||
gphttp.WriteBody(w, []byte(pkg.GetVersion()))
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
|
||||
"github.com/go-acme/lego/v4/certcrypto"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
|
@ -30,17 +30,17 @@ type (
|
|||
)
|
||||
|
||||
var (
|
||||
ErrMissingDomain = E.New("missing field 'domains'")
|
||||
ErrMissingEmail = E.New("missing field 'email'")
|
||||
ErrMissingProvider = E.New("missing field 'provider'")
|
||||
ErrInvalidDomain = E.New("invalid domain")
|
||||
ErrUnknownProvider = E.New("unknown provider")
|
||||
ErrMissingDomain = gperr.New("missing field 'domains'")
|
||||
ErrMissingEmail = gperr.New("missing field 'email'")
|
||||
ErrMissingProvider = gperr.New("missing field 'provider'")
|
||||
ErrInvalidDomain = gperr.New("invalid domain")
|
||||
ErrUnknownProvider = gperr.New("unknown provider")
|
||||
)
|
||||
|
||||
var domainOrWildcardRE = regexp.MustCompile(`^\*?([^.]+\.)+[^.]+$`)
|
||||
|
||||
// Validate implements the utils.CustomValidator interface.
|
||||
func (cfg *AutocertConfig) Validate() E.Error {
|
||||
func (cfg *AutocertConfig) Validate() gperr.Error {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ func (cfg *AutocertConfig) Validate() E.Error {
|
|||
return nil
|
||||
}
|
||||
|
||||
b := E.NewBuilder("autocert errors")
|
||||
b := gperr.NewBuilder("autocert errors")
|
||||
if cfg.Provider != ProviderLocal {
|
||||
if len(cfg.Domains) == 0 {
|
||||
b.Add(ErrMissingDomain)
|
||||
|
@ -79,7 +79,7 @@ func (cfg *AutocertConfig) Validate() E.Error {
|
|||
return b.Error()
|
||||
}
|
||||
|
||||
func (cfg *AutocertConfig) GetProvider() (*Provider, E.Error) {
|
||||
func (cfg *AutocertConfig) GetProvider() (*Provider, gperr.Error) {
|
||||
if cfg == nil {
|
||||
cfg = new(AutocertConfig)
|
||||
}
|
||||
|
@ -107,10 +107,10 @@ func (cfg *AutocertConfig) GetProvider() (*Provider, E.Error) {
|
|||
logging.Info().Msg("generate new ACME private key")
|
||||
privKey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
if err != nil {
|
||||
return nil, E.New("generate ACME private key").With(err)
|
||||
return nil, gperr.New("generate ACME private key").With(err)
|
||||
}
|
||||
if err = cfg.saveACMEKey(privKey); err != nil {
|
||||
return nil, E.New("save ACME private key").With(err)
|
||||
return nil, gperr.New("save ACME private key").With(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
|
@ -14,7 +15,7 @@ import (
|
|||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/go-acme/lego/v4/registration"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
U "github.com/yusing/go-proxy/internal/utils"
|
||||
|
@ -32,7 +33,7 @@ type (
|
|||
tlsCert *tls.Certificate
|
||||
certExpiries CertExpiries
|
||||
}
|
||||
ProviderGenerator func(ProviderOpt) (challenge.Provider, E.Error)
|
||||
ProviderGenerator func(ProviderOpt) (challenge.Provider, gperr.Error)
|
||||
|
||||
CertExpiries map[string]time.Time
|
||||
)
|
||||
|
@ -62,7 +63,7 @@ func (p *Provider) GetExpiries() CertExpiries {
|
|||
return p.certExpiries
|
||||
}
|
||||
|
||||
func (p *Provider) ObtainCert() E.Error {
|
||||
func (p *Provider) ObtainCert() error {
|
||||
if p.cfg.Provider == ProviderLocal {
|
||||
return nil
|
||||
}
|
||||
|
@ -75,7 +76,7 @@ func (p *Provider) ObtainCert() E.Error {
|
|||
|
||||
if p.user.Registration == nil {
|
||||
if err := p.registerACME(); err != nil {
|
||||
return E.From(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -100,22 +101,22 @@ func (p *Provider) ObtainCert() E.Error {
|
|||
Bundle: true,
|
||||
})
|
||||
if err != nil {
|
||||
return E.From(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err = p.saveCert(cert); err != nil {
|
||||
return E.From(err)
|
||||
return err
|
||||
}
|
||||
|
||||
tlsCert, err := tls.X509KeyPair(cert.Certificate, cert.PrivateKey)
|
||||
if err != nil {
|
||||
return E.From(err)
|
||||
return err
|
||||
}
|
||||
|
||||
expiries, err := getCertExpiries(&tlsCert)
|
||||
if err != nil {
|
||||
return E.From(err)
|
||||
return err
|
||||
}
|
||||
p.tlsCert = &tlsCert
|
||||
p.certExpiries = expiries
|
||||
|
@ -123,14 +124,14 @@ func (p *Provider) ObtainCert() E.Error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *Provider) LoadCert() E.Error {
|
||||
func (p *Provider) LoadCert() error {
|
||||
cert, err := tls.LoadX509KeyPair(p.cfg.CertPath, p.cfg.KeyPath)
|
||||
if err != nil {
|
||||
return E.Errorf("load SSL certificate: %w", err)
|
||||
return fmt.Errorf("load SSL certificate: %w", err)
|
||||
}
|
||||
expiries, err := getCertExpiries(&cert)
|
||||
if err != nil {
|
||||
return E.Errorf("parse SSL certificate: %w", err)
|
||||
return fmt.Errorf("parse SSL certificate: %w", err)
|
||||
}
|
||||
p.tlsCert = &cert
|
||||
p.certExpiries = expiries
|
||||
|
@ -171,7 +172,7 @@ func (p *Provider) ScheduleRenewal(parent task.Parent) {
|
|||
continue
|
||||
}
|
||||
if err := p.renewIfNeeded(); err != nil {
|
||||
E.LogWarn("cert renew failed", err)
|
||||
gperr.LogWarn("cert renew failed", err)
|
||||
lastErrOn = time.Now()
|
||||
continue
|
||||
}
|
||||
|
@ -184,10 +185,10 @@ func (p *Provider) ScheduleRenewal(parent task.Parent) {
|
|||
}()
|
||||
}
|
||||
|
||||
func (p *Provider) initClient() E.Error {
|
||||
func (p *Provider) initClient() error {
|
||||
legoClient, err := lego.NewClient(p.legoCfg)
|
||||
if err != nil {
|
||||
return E.From(err)
|
||||
return err
|
||||
}
|
||||
|
||||
generator := providersGenMap[p.cfg.Provider]
|
||||
|
@ -198,7 +199,7 @@ func (p *Provider) initClient() E.Error {
|
|||
|
||||
err = legoClient.Challenge.SetDNS01Provider(legoProvider)
|
||||
if err != nil {
|
||||
return E.From(err)
|
||||
return err
|
||||
}
|
||||
|
||||
p.client = legoClient
|
||||
|
@ -273,7 +274,7 @@ func (p *Provider) certState() CertState {
|
|||
return CertStateValid
|
||||
}
|
||||
|
||||
func (p *Provider) renewIfNeeded() E.Error {
|
||||
func (p *Provider) renewIfNeeded() error {
|
||||
if p.cfg.Provider == ProviderLocal {
|
||||
return nil
|
||||
}
|
||||
|
@ -312,13 +313,13 @@ func providerGenerator[CT any, PT challenge.Provider](
|
|||
defaultCfg func() *CT,
|
||||
newProvider func(*CT) (PT, error),
|
||||
) ProviderGenerator {
|
||||
return func(opt ProviderOpt) (challenge.Provider, E.Error) {
|
||||
return func(opt ProviderOpt) (challenge.Provider, gperr.Error) {
|
||||
cfg := defaultCfg()
|
||||
err := U.Deserialize(opt, &cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p, pErr := newProvider(cfg)
|
||||
return p, E.From(pErr)
|
||||
return p, gperr.Wrap(pErr)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
package autocert
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
func (p *Provider) Setup() (err E.Error) {
|
||||
func (p *Provider) Setup() (err error) {
|
||||
if err = p.LoadCert(); err != nil {
|
||||
if !err.Is(os.ErrNotExist) { // ignore if cert doesn't exist
|
||||
if !errors.Is(err, os.ErrNotExist) { // ignore if cert doesn't exist
|
||||
return err
|
||||
}
|
||||
logging.Debug().Msg("obtaining cert due to error loading cert")
|
||||
|
|
|
@ -4,12 +4,6 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
ConnectionTimeout = 5 * time.Second
|
||||
DialTimeout = 3 * time.Second
|
||||
KeepAlive = 60 * time.Second
|
||||
)
|
||||
|
||||
// file, folder structure
|
||||
|
||||
const (
|
||||
|
@ -50,5 +44,3 @@ const (
|
|||
StopTimeoutDefault = "30s"
|
||||
StopMethodDefault = "stop"
|
||||
)
|
||||
|
||||
const HeaderCheckRedirect = "X-Goproxy-Check-Redirect"
|
||||
|
|
|
@ -2,7 +2,7 @@ package config
|
|||
|
||||
import (
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/route/provider"
|
||||
"github.com/yusing/go-proxy/internal/utils/functional"
|
||||
)
|
||||
|
@ -29,12 +29,12 @@ func (cfg *Config) GetAgent(agentAddrOrDockerHost string) (*agent.AgentConfig, b
|
|||
return GetAgent(agent.GetAgentAddrFromDockerHost(agentAddrOrDockerHost))
|
||||
}
|
||||
|
||||
func (cfg *Config) AddAgent(host string, ca agent.PEMPair, client agent.PEMPair) (int, E.Error) {
|
||||
func (cfg *Config) AddAgent(host string, ca agent.PEMPair, client agent.PEMPair) (int, gperr.Error) {
|
||||
var agentCfg agent.AgentConfig
|
||||
agentCfg.Addr = host
|
||||
err := agentCfg.StartWithCerts(cfg.Task(), ca.Cert, client.Cert, client.Key)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return 0, gperr.Wrap(err, "failed to start agent")
|
||||
}
|
||||
|
||||
provider := provider.NewAgentProvider(&agentCfg)
|
||||
|
|
|
@ -14,9 +14,9 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/common"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/entrypoint"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/net/http/server"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/server"
|
||||
"github.com/yusing/go-proxy/internal/notif"
|
||||
proxy "github.com/yusing/go-proxy/internal/route/provider"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
|
@ -64,7 +64,7 @@ func newConfig() *Config {
|
|||
}
|
||||
}
|
||||
|
||||
func Load() (*Config, E.Error) {
|
||||
func Load() (*Config, gperr.Error) {
|
||||
if config.HasInstance() {
|
||||
panic(errors.New("config already loaded"))
|
||||
}
|
||||
|
@ -84,8 +84,8 @@ func WatchChanges() {
|
|||
t,
|
||||
configEventFlushInterval,
|
||||
OnConfigChange,
|
||||
func(err E.Error) {
|
||||
E.LogError("config reload error", err)
|
||||
func(err gperr.Error) {
|
||||
gperr.LogError("config reload error", err)
|
||||
},
|
||||
)
|
||||
eventQueue.Start(cfgWatcher.Events(t.Context()))
|
||||
|
@ -109,7 +109,7 @@ func OnConfigChange(ev []events.Event) {
|
|||
}
|
||||
}
|
||||
|
||||
func Reload() E.Error {
|
||||
func Reload() gperr.Error {
|
||||
// avoid race between config change and API reload request
|
||||
reloadMu.Lock()
|
||||
defer reloadMu.Unlock()
|
||||
|
@ -118,7 +118,7 @@ func Reload() E.Error {
|
|||
err := newCfg.load()
|
||||
if err != nil {
|
||||
newCfg.task.Finish(err)
|
||||
return E.New("using last config").With(err)
|
||||
return gperr.New("using last config").With(err)
|
||||
}
|
||||
|
||||
// cancel all current subtasks -> wait
|
||||
|
@ -133,10 +133,13 @@ func (cfg *Config) Value() *config.Config {
|
|||
return cfg.value
|
||||
}
|
||||
|
||||
func (cfg *Config) Reload() E.Error {
|
||||
func (cfg *Config) Reload() gperr.Error {
|
||||
return Reload()
|
||||
}
|
||||
|
||||
// AutoCertProvider returns the autocert provider.
|
||||
//
|
||||
// If the autocert provider is not configured, it returns nil.
|
||||
func (cfg *Config) AutoCertProvider() *autocert.Provider {
|
||||
return cfg.autocertProvider
|
||||
}
|
||||
|
@ -163,7 +166,7 @@ func (cfg *Config) StartAutoCert() {
|
|||
}
|
||||
|
||||
if err := autocert.Setup(); err != nil {
|
||||
E.LogFatal("autocert setup error", err)
|
||||
gperr.LogFatal("autocert setup error", err)
|
||||
} else {
|
||||
autocert.ScheduleRenewal(cfg.task)
|
||||
}
|
||||
|
@ -175,8 +178,8 @@ func (cfg *Config) StartProxyProviders() {
|
|||
return p.Start(cfg.task)
|
||||
})
|
||||
|
||||
if err := E.Join(errs...); err != nil {
|
||||
E.LogError("route provider errors", err)
|
||||
if err := gperr.Join(errs...); err != nil {
|
||||
gperr.LogError("route provider errors", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,21 +213,21 @@ func (cfg *Config) StartServers(opts ...*StartServersOptions) {
|
|||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) load() E.Error {
|
||||
func (cfg *Config) load() gperr.Error {
|
||||
const errMsg = "config load error"
|
||||
|
||||
data, err := os.ReadFile(common.ConfigPath)
|
||||
if err != nil {
|
||||
E.LogFatal(errMsg, err)
|
||||
gperr.LogFatal(errMsg, err)
|
||||
}
|
||||
|
||||
model := config.DefaultConfig()
|
||||
if err := utils.DeserializeYAML(data, model); err != nil {
|
||||
E.LogFatal(errMsg, err)
|
||||
gperr.LogFatal(errMsg, err)
|
||||
}
|
||||
|
||||
// errors are non fatal below
|
||||
errs := E.NewBuilder(errMsg)
|
||||
errs := gperr.NewBuilder(errMsg)
|
||||
errs.Add(cfg.entrypoint.SetMiddlewares(model.Entrypoint.Middlewares))
|
||||
errs.Add(cfg.entrypoint.SetAccessLogger(cfg.task, model.Entrypoint.AccessLog))
|
||||
cfg.initNotification(model.Providers.Notification)
|
||||
|
@ -252,7 +255,7 @@ func (cfg *Config) initNotification(notifCfg []notif.NotificationConfig) {
|
|||
}
|
||||
}
|
||||
|
||||
func (cfg *Config) initAutoCert(autocertCfg *autocert.AutocertConfig) (err E.Error) {
|
||||
func (cfg *Config) initAutoCert(autocertCfg *autocert.AutocertConfig) (err gperr.Error) {
|
||||
if cfg.autocertProvider != nil {
|
||||
return
|
||||
}
|
||||
|
@ -261,9 +264,9 @@ func (cfg *Config) initAutoCert(autocertCfg *autocert.AutocertConfig) (err E.Err
|
|||
return
|
||||
}
|
||||
|
||||
func (cfg *Config) errIfExists(p *proxy.Provider) E.Error {
|
||||
func (cfg *Config) errIfExists(p *proxy.Provider) gperr.Error {
|
||||
if _, ok := cfg.providers.Load(p.String()); ok {
|
||||
return E.Errorf("provider %s already exists", p.String())
|
||||
return gperr.Errorf("provider %s already exists", p.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -272,9 +275,9 @@ func (cfg *Config) storeProvider(p *proxy.Provider) {
|
|||
cfg.providers.Store(p.String(), p)
|
||||
}
|
||||
|
||||
func (cfg *Config) loadRouteProviders(providers *config.Providers) E.Error {
|
||||
errs := E.NewBuilder("route provider errors")
|
||||
results := E.NewBuilder("loaded route providers")
|
||||
func (cfg *Config) loadRouteProviders(providers *config.Providers) gperr.Error {
|
||||
errs := gperr.NewBuilder("route provider errors")
|
||||
results := gperr.NewBuilder("loaded route providers")
|
||||
|
||||
removeAllAgents()
|
||||
|
||||
|
@ -297,7 +300,7 @@ func (cfg *Config) loadRouteProviders(providers *config.Providers) E.Error {
|
|||
err = cfg.errIfExists(p)
|
||||
}
|
||||
if err != nil {
|
||||
errs.Add(E.PrependSubject(filename, err))
|
||||
errs.Add(gperr.PrependSubject(filename, err))
|
||||
continue
|
||||
}
|
||||
cfg.storeProvider(p)
|
||||
|
|
|
@ -8,11 +8,10 @@ import (
|
|||
"github.com/go-playground/validator/v10"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/internal/autocert"
|
||||
"github.com/yusing/go-proxy/internal/net/http/accesslog"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/accesslog"
|
||||
"github.com/yusing/go-proxy/internal/notif"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -37,12 +36,12 @@ type (
|
|||
|
||||
ConfigInstance interface {
|
||||
Value() *Config
|
||||
Reload() E.Error
|
||||
Reload() gperr.Error
|
||||
Statistics() map[string]any
|
||||
RouteProviderList() []string
|
||||
Context() context.Context
|
||||
GetAgent(agentAddrOrDockerHost string) (*agent.AgentConfig, bool)
|
||||
AddAgent(host string, ca agent.PEMPair, client agent.PEMPair) (int, E.Error)
|
||||
AddAgent(host string, ca agent.PEMPair, client agent.PEMPair) (int, gperr.Error)
|
||||
ListAgents() []*agent.AgentConfig
|
||||
}
|
||||
)
|
||||
|
@ -79,7 +78,7 @@ func HasInstance() bool {
|
|||
return instance != nil
|
||||
}
|
||||
|
||||
func Validate(data []byte) E.Error {
|
||||
func Validate(data []byte) gperr.Error {
|
||||
var model Config
|
||||
return utils.DeserializeYAML(data, &model)
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
)
|
||||
|
||||
type templateData struct {
|
||||
|
@ -23,11 +23,11 @@ func (w *Watcher) makeLoadingPageBody() []byte {
|
|||
msg := w.ContainerName + " is starting..."
|
||||
|
||||
data := new(templateData)
|
||||
data.CheckRedirectHeader = common.HeaderCheckRedirect
|
||||
data.CheckRedirectHeader = httpheaders.HeaderGoDoxyCheckRedirect
|
||||
data.Title = w.ContainerName
|
||||
data.Message = strings.ReplaceAll(msg, " ", " ")
|
||||
|
||||
buf := bytes.NewBuffer(make([]byte, len(loadingPage)+len(data.Title)+len(data.Message)+len(common.HeaderCheckRedirect)))
|
||||
buf := bytes.NewBuffer(make([]byte, len(loadingPage)+len(data.Title)+len(data.Message)+len(httpheaders.HeaderGoDoxyCheckRedirect)))
|
||||
err := loadingPageTmpl.Execute(buf, data)
|
||||
if err != nil { // should never happen in production
|
||||
panic(err)
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/docker"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -40,7 +40,7 @@ var validSignals = map[string]struct{}{
|
|||
"INT": {}, "TERM": {}, "HUP": {}, "QUIT": {},
|
||||
}
|
||||
|
||||
func ValidateConfig(cont *docker.Container) (*Config, E.Error) {
|
||||
func ValidateConfig(cont *docker.Container) (*Config, gperr.Error) {
|
||||
if cont == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -54,14 +54,14 @@ func ValidateConfig(cont *docker.Container) (*Config, E.Error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
errs := E.NewBuilder("invalid idlewatcher config")
|
||||
errs := gperr.NewBuilder("invalid idlewatcher config")
|
||||
|
||||
idleTimeout := E.Collect(errs, validateDurationPostitive, cont.IdleTimeout)
|
||||
wakeTimeout := E.Collect(errs, validateDurationPostitive, cont.WakeTimeout)
|
||||
stopTimeout := E.Collect(errs, validateDurationPostitive, cont.StopTimeout)
|
||||
stopMethod := E.Collect(errs, validateStopMethod, cont.StopMethod)
|
||||
signal := E.Collect(errs, validateSignal, cont.StopSignal)
|
||||
startEndpoint := E.Collect(errs, validateStartEndpoint, cont.StartEndpoint)
|
||||
idleTimeout := gperr.Collect(errs, validateDurationPostitive, cont.IdleTimeout)
|
||||
wakeTimeout := gperr.Collect(errs, validateDurationPostitive, cont.WakeTimeout)
|
||||
stopTimeout := gperr.Collect(errs, validateDurationPostitive, cont.StopTimeout)
|
||||
stopMethod := gperr.Collect(errs, validateStopMethod, cont.StopMethod)
|
||||
signal := gperr.Collect(errs, validateSignal, cont.StopSignal)
|
||||
startEndpoint := gperr.Collect(errs, validateStartEndpoint, cont.StartEndpoint)
|
||||
|
||||
if errs.HasError() {
|
||||
return nil, errs.Error()
|
||||
|
|
|
@ -5,9 +5,9 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/docker/idlewatcher/types"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/metrics"
|
||||
"github.com/yusing/go-proxy/internal/net/http/reverseproxy"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/reverseproxy"
|
||||
net "github.com/yusing/go-proxy/internal/net/types"
|
||||
route "github.com/yusing/go-proxy/internal/route/types"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
|
@ -37,7 +37,7 @@ const (
|
|||
|
||||
// TODO: support stream
|
||||
|
||||
func newWaker(parent task.Parent, route route.Route, rp *reverseproxy.ReverseProxy, stream net.Stream) (Waker, E.Error) {
|
||||
func newWaker(parent task.Parent, route route.Route, rp *reverseproxy.ReverseProxy, stream net.Stream) (Waker, gperr.Error) {
|
||||
hcCfg := route.HealthCheckConfig()
|
||||
hcCfg.Timeout = idleWakerCheckTimeout
|
||||
|
||||
|
@ -48,7 +48,7 @@ func newWaker(parent task.Parent, route route.Route, rp *reverseproxy.ReversePro
|
|||
task := parent.Subtask("idlewatcher." + route.TargetName())
|
||||
watcher, err := registerWatcher(task, route, waker)
|
||||
if err != nil {
|
||||
return nil, E.Errorf("register watcher: %w", err)
|
||||
return nil, gperr.Errorf("register watcher: %w", err)
|
||||
}
|
||||
|
||||
switch {
|
||||
|
@ -66,16 +66,16 @@ func newWaker(parent task.Parent, route route.Route, rp *reverseproxy.ReversePro
|
|||
}
|
||||
|
||||
// lifetime should follow route provider.
|
||||
func NewHTTPWaker(parent task.Parent, route route.Route, rp *reverseproxy.ReverseProxy) (Waker, E.Error) {
|
||||
func NewHTTPWaker(parent task.Parent, route route.Route, rp *reverseproxy.ReverseProxy) (Waker, gperr.Error) {
|
||||
return newWaker(parent, route, rp, nil)
|
||||
}
|
||||
|
||||
func NewStreamWaker(parent task.Parent, route route.Route, stream net.Stream) (Waker, E.Error) {
|
||||
func NewStreamWaker(parent task.Parent, route route.Route, stream net.Stream) (Waker, gperr.Error) {
|
||||
return newWaker(parent, route, nil, stream)
|
||||
}
|
||||
|
||||
// Start implements health.HealthMonitor.
|
||||
func (w *Watcher) Start(parent task.Parent) E.Error {
|
||||
func (w *Watcher) Start(parent task.Parent) gperr.Error {
|
||||
w.task.OnCancel("route_cleanup", func() {
|
||||
parent.Finish(w.task.FinishCause())
|
||||
if w.metric != nil {
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
gphttp "github.com/yusing/go-proxy/internal/net/http"
|
||||
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
)
|
||||
|
||||
|
@ -63,7 +63,7 @@ func (w *Watcher) wakeFromHTTP(rw http.ResponseWriter, r *http.Request) (shouldN
|
|||
accept := gphttp.GetAccept(r.Header)
|
||||
acceptHTML := (r.Method == http.MethodGet && accept.AcceptHTML() || r.RequestURI == "/" && accept.IsEmpty())
|
||||
|
||||
isCheckRedirect := r.Header.Get(common.HeaderCheckRedirect) != ""
|
||||
isCheckRedirect := r.Header.Get(httpheaders.HeaderGoDoxyCheckRedirect) != ""
|
||||
if !isCheckRedirect && acceptHTML {
|
||||
// Send a loading response to the client
|
||||
body := w.makeLoadingPageBody()
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/rs/zerolog"
|
||||
D "github.com/yusing/go-proxy/internal/docker"
|
||||
idlewatcher "github.com/yusing/go-proxy/internal/docker/idlewatcher/types"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
route "github.com/yusing/go-proxy/internal/route/types"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
|
@ -180,7 +180,7 @@ func (w *Watcher) wakeIfStopped() error {
|
|||
case "running":
|
||||
return nil
|
||||
default:
|
||||
return E.Errorf("unexpected container status: %s", status)
|
||||
return gperr.Errorf("unexpected container status: %s", status)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,7 +213,7 @@ func (w *Watcher) expires() time.Time {
|
|||
return w.lastReset.Add(w.IdleTimeout)
|
||||
}
|
||||
|
||||
func (w *Watcher) getEventCh(dockerWatcher watcher.DockerWatcher) (eventCh <-chan events.Event, errCh <-chan E.Error) {
|
||||
func (w *Watcher) getEventCh(dockerWatcher watcher.DockerWatcher) (eventCh <-chan events.Event, errCh <-chan gperr.Error) {
|
||||
eventCh, errCh = dockerWatcher.EventsWithOptions(w.Task().Context(), watcher.DockerListOptions{
|
||||
Filters: watcher.NewDockerFilter(
|
||||
watcher.DockerFilterContainer,
|
||||
|
@ -251,7 +251,7 @@ func (w *Watcher) watchUntilDestroy() (returnCause error) {
|
|||
return w.task.FinishCause()
|
||||
case err := <-dockerEventErrCh:
|
||||
if !err.Is(context.Canceled) {
|
||||
E.LogError("idlewatcher error", err, &w.Logger)
|
||||
gperr.LogError("idlewatcher error", err, &w.Logger)
|
||||
}
|
||||
return err
|
||||
case e := <-dockerEventCh:
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
package docker
|
||||
|
||||
import (
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
type LabelMap = map[string]any
|
||||
|
||||
var ErrInvalidLabel = E.New("invalid label")
|
||||
var ErrInvalidLabel = gperr.New("invalid label")
|
||||
|
||||
func ParseLabels(labels map[string]string) (LabelMap, E.Error) {
|
||||
func ParseLabels(labels map[string]string) (LabelMap, gperr.Error) {
|
||||
nestedMap := make(LabelMap)
|
||||
errs := E.NewBuilder("labels error")
|
||||
errs := gperr.NewBuilder("labels error")
|
||||
|
||||
for lbl, value := range labels {
|
||||
parts := strutils.SplitRune(lbl, '.')
|
||||
|
@ -37,7 +37,7 @@ func ParseLabels(labels map[string]string) (LabelMap, E.Error) {
|
|||
// Move deeper into the nested map
|
||||
m, ok := currentMap[k].(LabelMap)
|
||||
if !ok && currentMap[k] != "" {
|
||||
errs.Add(E.Errorf("expect mapping, got %T", currentMap[k]).Subject(lbl))
|
||||
errs.Add(gperr.Errorf("expect mapping, got %T", currentMap[k]).Subject(lbl))
|
||||
continue
|
||||
} else if !ok {
|
||||
m = make(LabelMap)
|
||||
|
|
|
@ -7,10 +7,10 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
gphttp "github.com/yusing/go-proxy/internal/net/http"
|
||||
"github.com/yusing/go-proxy/internal/net/http/accesslog"
|
||||
"github.com/yusing/go-proxy/internal/net/http/middleware"
|
||||
"github.com/yusing/go-proxy/internal/net/http/middleware/errorpage"
|
||||
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/accesslog"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/middleware"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/middleware/errorpage"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
route "github.com/yusing/go-proxy/internal/route/types"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
|
|
106
internal/gperr/README.md
Normal file
106
internal/gperr/README.md
Normal file
|
@ -0,0 +1,106 @@
|
|||
# gperr
|
||||
|
||||
gperr is an error interface that supports nested structure and subject highlighting.
|
||||
|
||||
## Usage
|
||||
|
||||
### gperr.Error
|
||||
|
||||
The error interface.
|
||||
|
||||
### gperr.New
|
||||
|
||||
Like `errors.New`, but returns a `gperr.Error`.
|
||||
|
||||
### gperr.Wrap
|
||||
|
||||
Like `fmt.Errorf("%s: %w", message, err)`, but returns a `gperr.Error`.
|
||||
|
||||
### gperr.Error.Subject
|
||||
|
||||
Returns a new error with the subject prepended to the error message. The main subject is highlighted.
|
||||
|
||||
```go
|
||||
err := gperr.New("error message")
|
||||
err = err.Subject("bar")
|
||||
err = err.Subject("foo")
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
<code>foo > <span style="color: red;">bar</span>: error message</code>
|
||||
|
||||
### gperr.Error.Subjectf
|
||||
|
||||
Like `gperr.Error.Subject`, but formats the subject with `fmt.Sprintf`.
|
||||
|
||||
### gperr.PrependSubject
|
||||
|
||||
Prepends the subject to the error message like `gperr.Error.Subject`.
|
||||
|
||||
```go
|
||||
err := gperr.New("error message")
|
||||
err = gperr.PrependSubject(err, "foo")
|
||||
err = gperr.PrependSubject(err, "bar")
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
<code>bar > <span style="color: red;">foo</span>: error message</code>
|
||||
|
||||
### gperr.Error.With
|
||||
|
||||
Adds a new error to the error chain.
|
||||
|
||||
```go
|
||||
err := gperr.New("error message")
|
||||
err = err.With(gperr.New("inner error"))
|
||||
err = err.With(gperr.New("inner error 2").With(gperr.New("inner inner error")))
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
error message:
|
||||
• inner error
|
||||
• inner error 2
|
||||
• inner inner error
|
||||
```
|
||||
|
||||
### gperr.Error.Withf
|
||||
|
||||
Like `gperr.Error.With`, but formats the error with `fmt.Errorf`.
|
||||
|
||||
### gperr.Error.Is
|
||||
|
||||
Returns true if the error is equal to the given error.
|
||||
|
||||
### gperr.Builder
|
||||
|
||||
A builder for `gperr.Error`.
|
||||
|
||||
```go
|
||||
builder := gperr.NewBuilder("foo")
|
||||
builder.Add(gperr.New("error message"))
|
||||
builder.Addf("error message: %s", "foo")
|
||||
builder.AddRange(gperr.New("error message 1"), gperr.New("error message 2"))
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
foo:
|
||||
• error message
|
||||
• error message: foo
|
||||
• error message 1
|
||||
• error message 2
|
||||
```
|
||||
|
||||
### gperr.Builder.Build
|
||||
|
||||
Builds a `gperr.Error` from the builder.
|
||||
|
||||
## When to return gperr.Error
|
||||
|
||||
- When you want to return multiple errors
|
||||
- When the error has a subject
|
|
@ -1,4 +1,4 @@
|
|||
package err
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
|
@ -1,4 +1,4 @@
|
|||
package err
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -36,7 +36,7 @@ func (b *Builder) error() Error {
|
|||
|
||||
func (b *Builder) Error() Error {
|
||||
if len(b.errs) == 1 {
|
||||
return From(b.errs[0])
|
||||
return wrap(b.errs[0])
|
||||
}
|
||||
return b.error()
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ func (b *Builder) Add(err error) *Builder {
|
|||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
switch err := From(err).(type) {
|
||||
switch err := wrap(err).(type) {
|
||||
case *baseError:
|
||||
b.errs = append(b.errs, err.Err)
|
||||
case *nestedError:
|
|
@ -1,4 +1,4 @@
|
|||
package err_test
|
||||
package gperr_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -6,7 +6,7 @@ import (
|
|||
"io"
|
||||
"testing"
|
||||
|
||||
. "github.com/yusing/go-proxy/internal/error"
|
||||
. "github.com/yusing/go-proxy/internal/gperr"
|
||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||
)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package err
|
||||
package gperr
|
||||
|
||||
type Error interface {
|
||||
error
|
|
@ -1,4 +1,4 @@
|
|||
package err
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -44,7 +44,7 @@ func TestBaseWithExtra(t *testing.T) {
|
|||
|
||||
func TestBaseUnwrap(t *testing.T) {
|
||||
err := errors.New("err")
|
||||
wrapped := From(err)
|
||||
wrapped := Wrap(err)
|
||||
|
||||
ExpectError(t, err, errors.Unwrap(wrapped))
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ func TestBaseUnwrap(t *testing.T) {
|
|||
func TestNestedUnwrap(t *testing.T) {
|
||||
err := errors.New("err")
|
||||
err2 := New("err2")
|
||||
wrapped := From(err).Subject("foo").With(err2.Subject("bar"))
|
||||
wrapped := Wrap(err).Subject("foo").With(err2.Subject("bar"))
|
||||
|
||||
unwrapper, ok := wrapped.(interface{ Unwrap() []error })
|
||||
ExpectTrue(t, ok)
|
||||
|
@ -64,7 +64,7 @@ func TestNestedUnwrap(t *testing.T) {
|
|||
|
||||
func TestErrorIs(t *testing.T) {
|
||||
from := errors.New("error")
|
||||
err := From(from)
|
||||
err := Wrap(from)
|
||||
ExpectError(t, from, err)
|
||||
|
||||
ExpectTrue(t, err.Is(from))
|
|
@ -1,4 +1,4 @@
|
|||
package err
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
|
@ -6,42 +6,35 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/logging"
|
||||
)
|
||||
|
||||
func getLogger(logger ...*zerolog.Logger) *zerolog.Logger {
|
||||
func log(msg string, err error, level zerolog.Level, logger ...*zerolog.Logger) {
|
||||
var l *zerolog.Logger
|
||||
if len(logger) > 0 {
|
||||
return logger[0]
|
||||
l = logger[0]
|
||||
} else {
|
||||
l = logging.GetLogger()
|
||||
}
|
||||
return logging.GetLogger()
|
||||
l.WithLevel(level).Msg(msg + ": " + err.Error())
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func LogFatal(msg string, err error, logger ...*zerolog.Logger) {
|
||||
if common.IsDebug {
|
||||
LogPanic(msg, err, logger...)
|
||||
}
|
||||
getLogger(logger...).Fatal().Msg(err.Error())
|
||||
log(msg, err, zerolog.FatalLevel, logger...)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func LogError(msg string, err error, logger ...*zerolog.Logger) {
|
||||
getLogger(logger...).Error().Msg(err.Error())
|
||||
log(msg, err, zerolog.ErrorLevel, logger...)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func LogWarn(msg string, err error, logger ...*zerolog.Logger) {
|
||||
getLogger(logger...).Warn().Msg(err.Error())
|
||||
log(msg, err, zerolog.WarnLevel, logger...)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func LogPanic(msg string, err error, logger ...*zerolog.Logger) {
|
||||
getLogger(logger...).Panic().Msg(err.Error())
|
||||
log(msg, err, zerolog.PanicLevel, logger...)
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func LogInfo(msg string, err error, logger ...*zerolog.Logger) {
|
||||
getLogger(logger...).Info().Msg(err.Error())
|
||||
}
|
||||
|
||||
//go:inline
|
||||
func LogDebug(msg string, err error, logger ...*zerolog.Logger) {
|
||||
getLogger(logger...).Debug().Msg(err.Error())
|
||||
log(msg, err, zerolog.DebugLevel, logger...)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package err
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -99,7 +99,7 @@ func makeLines(errs []error, level int) []string {
|
|||
}
|
||||
lines := make([]string, 0, len(errs))
|
||||
for _, err := range errs {
|
||||
switch err := From(err).(type) {
|
||||
switch err := wrap(err).(type) {
|
||||
case *nestedError:
|
||||
if err.Err != nil {
|
||||
lines = append(lines, makeLine(err.Err.Error(), level))
|
|
@ -1,4 +1,4 @@
|
|||
package err
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
|
@ -1,6 +1,8 @@
|
|||
package err
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
|
@ -19,27 +21,50 @@ func Errorf(format string, args ...any) Error {
|
|||
return &baseError{fmt.Errorf(format, args...)}
|
||||
}
|
||||
|
||||
// Wrap wraps message in front of the error message.
|
||||
func Wrap(err error, message ...string) Error {
|
||||
if len(message) == 0 || message[0] == "" {
|
||||
return From(err)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return Errorf("%s: %w", message[0], err)
|
||||
if len(message) == 0 || message[0] == "" {
|
||||
return wrap(err)
|
||||
}
|
||||
//nolint:errorlint
|
||||
switch err := err.(type) {
|
||||
case *baseError:
|
||||
err.Err = fmt.Errorf("%s: %w", message[0], err.Err)
|
||||
return err
|
||||
case *nestedError:
|
||||
err.Err = fmt.Errorf("%s: %w", message[0], err.Err)
|
||||
return err
|
||||
}
|
||||
return &baseError{fmt.Errorf("%s: %w", message[0], err)}
|
||||
}
|
||||
|
||||
func From(err error) Error {
|
||||
func wrap(err error) Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
//nolint:errorlint
|
||||
switch err := err.(type) {
|
||||
case *baseError:
|
||||
return err
|
||||
case *nestedError:
|
||||
case Error:
|
||||
return err
|
||||
}
|
||||
return &baseError{err}
|
||||
}
|
||||
|
||||
func IsJSONMarshallable(err error) bool {
|
||||
switch err := err.(type) {
|
||||
case *nestedError, *withSubject:
|
||||
return true
|
||||
case *baseError:
|
||||
return IsJSONMarshallable(err.Err)
|
||||
default:
|
||||
var v json.Marshaler
|
||||
return errors.As(err, &v)
|
||||
}
|
||||
}
|
||||
|
||||
func Join(errors ...error) Error {
|
||||
n := 0
|
||||
for _, err := range errors {
|
58
internal/gperr/utils_test.go
Normal file
58
internal/gperr/utils_test.go
Normal file
|
@ -0,0 +1,58 @@
|
|||
package gperr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testErr struct{}
|
||||
|
||||
func (e *testErr) Error() string {
|
||||
return "test error"
|
||||
}
|
||||
|
||||
func (e *testErr) MarshalJSON() ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func TestIsJSONMarshallable(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
err error
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "testErr",
|
||||
err: &testErr{},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "baseError",
|
||||
err: &baseError{},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "nestedError",
|
||||
err: &nestedError{},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "withSubject",
|
||||
err: &withSubject{},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "standard error",
|
||||
err: errors.New("test error"),
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if got := IsJSONMarshallable(test.err); got != test.want {
|
||||
t.Errorf("IsJSONMarshallable(%v) = %v, want %v", test.err, got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/yusing/go-proxy/internal"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
)
|
||||
|
||||
type (
|
||||
|
@ -31,7 +31,7 @@ const (
|
|||
IconSourceSelfhSt
|
||||
)
|
||||
|
||||
var ErrInvalidIconURL = E.New("invalid icon url")
|
||||
var ErrInvalidIconURL = gperr.New("invalid icon url")
|
||||
|
||||
func NewSelfhStIconURL(reference, format string) *IconURL {
|
||||
return &IconURL{
|
||||
|
|
|
@ -11,9 +11,10 @@ import (
|
|||
|
||||
"github.com/coder/websocket"
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/gpwebsocket"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
F "github.com/yusing/go-proxy/internal/utils/functional"
|
||||
)
|
||||
|
@ -27,8 +28,9 @@ type memLogger struct {
|
|||
sync.RWMutex
|
||||
notifyLock sync.RWMutex
|
||||
connChans F.Map[chan *logEntryRange, struct{}]
|
||||
listeners F.Map[chan []byte, struct{}]
|
||||
|
||||
bufPool sync.Pool // used in hook mode
|
||||
bufPool sync.Pool
|
||||
}
|
||||
|
||||
type MemLogger io.Writer
|
||||
|
@ -41,15 +43,16 @@ const (
|
|||
maxMemLogSize = 16 * 1024
|
||||
truncateSize = maxMemLogSize / 2
|
||||
initialWriteChunkSize = 4 * 1024
|
||||
hookModeBufSize = 256
|
||||
bufPoolSize = 256
|
||||
)
|
||||
|
||||
var memLoggerInstance = &memLogger{
|
||||
connChans: F.NewMapOf[chan *logEntryRange, struct{}](),
|
||||
listeners: F.NewMapOf[chan []byte, struct{}](),
|
||||
bufPool: sync.Pool{
|
||||
New: func() any {
|
||||
return &buffer{
|
||||
data: make([]byte, 0, hookModeBufSize),
|
||||
data: make([]byte, 0, bufPoolSize),
|
||||
}
|
||||
},
|
||||
},
|
||||
|
@ -92,6 +95,10 @@ func HandlerFunc() http.HandlerFunc {
|
|||
return memLoggerInstance.ServeHTTP
|
||||
}
|
||||
|
||||
func Events() (<-chan []byte, func()) {
|
||||
return memLoggerInstance.events()
|
||||
}
|
||||
|
||||
func (m *memLogger) truncateIfNeeded(n int) {
|
||||
m.RLock()
|
||||
needTruncate := m.Len()+n > maxMemLogSize
|
||||
|
@ -111,7 +118,7 @@ func (m *memLogger) truncateIfNeeded(n int) {
|
|||
|
||||
func (m *memLogger) notifyWS(pos, n int) {
|
||||
if m.connChans.Size() > 0 {
|
||||
timeout := time.NewTimer(1 * time.Second)
|
||||
timeout := time.NewTimer(2 * time.Second)
|
||||
defer timeout.Stop()
|
||||
|
||||
m.notifyLock.RLock()
|
||||
|
@ -125,6 +132,19 @@ func (m *memLogger) notifyWS(pos, n int) {
|
|||
return false
|
||||
}
|
||||
})
|
||||
if m.listeners.Size() > 0 {
|
||||
msg := make([]byte, n)
|
||||
copy(msg, m.Buffer.Bytes()[pos:pos+n])
|
||||
m.listeners.Range(func(ch chan []byte, _ struct{}) bool {
|
||||
select {
|
||||
case <-timeout.C:
|
||||
logging.Warn().Msg("mem logger: timeout logging to channel")
|
||||
return false
|
||||
case ch <- msg:
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -153,9 +173,9 @@ func (m *memLogger) Write(p []byte) (n int, err error) {
|
|||
}
|
||||
|
||||
func (m *memLogger) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
conn, err := utils.InitiateWS(w, r)
|
||||
conn, err := gpwebsocket.Initiate(w, r)
|
||||
if err != nil {
|
||||
utils.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -172,13 +192,27 @@ func (m *memLogger) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
}()
|
||||
|
||||
if err := m.wsInitial(r.Context(), conn); err != nil {
|
||||
utils.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
m.wsStreamLog(r.Context(), conn, logCh)
|
||||
}
|
||||
|
||||
func (m *memLogger) events() (<-chan []byte, func()) {
|
||||
ch := make(chan []byte, 10)
|
||||
m.notifyLock.Lock()
|
||||
defer m.notifyLock.Unlock()
|
||||
m.listeners.Store(ch, struct{}{})
|
||||
|
||||
return ch, func() {
|
||||
m.notifyLock.Lock()
|
||||
defer m.notifyLock.Unlock()
|
||||
m.listeners.Delete(ch)
|
||||
close(ch)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *memLogger) writeBytes(ctx context.Context, conn *websocket.Conn, b []byte) error {
|
||||
return conn.Write(ctx, websocket.MessageText, b)
|
||||
}
|
||||
|
|
|
@ -7,9 +7,10 @@ import (
|
|||
|
||||
"github.com/coder/websocket"
|
||||
"github.com/coder/websocket/wsjson"
|
||||
"github.com/yusing/go-proxy/internal/api/v1/utils"
|
||||
metricsutils "github.com/yusing/go-proxy/internal/metrics/utils"
|
||||
"github.com/yusing/go-proxy/internal/net/http/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/gpwebsocket"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
)
|
||||
|
||||
// ServeHTTP serves the data for the given period.
|
||||
|
@ -36,7 +37,7 @@ func (p *Poller[T, AggregateT]) ServeHTTP(w http.ResponseWriter, r *http.Request
|
|||
if interval < minInterval {
|
||||
interval = minInterval
|
||||
}
|
||||
utils.PeriodicWS(w, r, interval, func(conn *websocket.Conn) error {
|
||||
gpwebsocket.Periodic(w, r, interval, func(conn *websocket.Conn) error {
|
||||
data, err := p.getRespData(r)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -49,14 +50,14 @@ func (p *Poller[T, AggregateT]) ServeHTTP(w http.ResponseWriter, r *http.Request
|
|||
} else {
|
||||
data, err := p.getRespData(r)
|
||||
if err != nil {
|
||||
utils.HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
if data == nil {
|
||||
http.Error(w, "no data", http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
utils.RespondJSON(w, r, data)
|
||||
gphttp.RespondJSON(w, r, data)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"net/url"
|
||||
"time"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
)
|
||||
|
@ -84,7 +84,7 @@ func (p *Poller[T, AggregateT]) gatherErrs() (string, bool) {
|
|||
if len(p.errs) == 0 {
|
||||
return "", false
|
||||
}
|
||||
errs := E.NewBuilder(fmt.Sprintf("poller %s has encountered %d errors in the last %s:", p.name, len(p.errs), gatherErrsInterval))
|
||||
errs := gperr.NewBuilder(fmt.Sprintf("poller %s has encountered %d errors in the last %s:", p.name, len(p.errs), gatherErrsInterval))
|
||||
for _, e := range p.errs {
|
||||
errs.Addf("%w: %d times", e.err, e.count)
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"github.com/shirou/gopsutil/v4/net"
|
||||
"github.com/shirou/gopsutil/v4/sensors"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/metrics/period"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
|
@ -40,7 +40,7 @@ func _() { // check if this behavior is not changed
|
|||
}
|
||||
|
||||
func getSystemInfo(ctx context.Context, lastResult *SystemInfo) (*SystemInfo, error) {
|
||||
errs := E.NewBuilder("failed to get system info")
|
||||
errs := gperr.NewBuilder("failed to get system info")
|
||||
var systemInfo SystemInfo
|
||||
|
||||
if !common.MetricsDisableCPU {
|
||||
|
@ -95,8 +95,8 @@ func getSystemInfo(ctx context.Context, lastResult *SystemInfo) (*SystemInfo, er
|
|||
}
|
||||
|
||||
if errs.HasError() {
|
||||
allWarnings := E.NewBuilder("")
|
||||
allErrors := E.NewBuilder("failed to get system info")
|
||||
allWarnings := gperr.NewBuilder("")
|
||||
allErrors := gperr.NewBuilder("failed to get system info")
|
||||
errs.ForEach(func(err error) {
|
||||
// disk.Warnings has the same type
|
||||
// all Warnings are alias of common.Warnings from "github.com/shirou/gopsutil/v4/internal/common"
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
)
|
||||
|
@ -131,7 +131,7 @@ func (l *AccessLogger) Flush(force bool) {
|
|||
}
|
||||
|
||||
func (l *AccessLogger) handleErr(err error) {
|
||||
E.LogError("failed to write access log", err)
|
||||
gperr.LogError("failed to write access log", err)
|
||||
}
|
||||
|
||||
func (l *AccessLogger) start() {
|
|
@ -9,7 +9,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/yusing/go-proxy/internal/net/http/accesslog"
|
||||
. "github.com/yusing/go-proxy/internal/net/gphttp/accesslog"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||
)
|
|
@ -4,7 +4,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/docker"
|
||||
. "github.com/yusing/go-proxy/internal/net/http/accesslog"
|
||||
. "github.com/yusing/go-proxy/internal/net/gphttp/accesslog"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||
)
|
|
@ -3,7 +3,7 @@ package accesslog_test
|
|||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/yusing/go-proxy/internal/net/http/accesslog"
|
||||
. "github.com/yusing/go-proxy/internal/net/gphttp/accesslog"
|
||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||
)
|
||||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
@ -27,7 +27,7 @@ type (
|
|||
CIDR struct{ types.CIDR }
|
||||
)
|
||||
|
||||
var ErrInvalidHTTPHeaderFilter = E.New("invalid http header filter")
|
||||
var ErrInvalidHTTPHeaderFilter = gperr.New("invalid http header filter")
|
||||
|
||||
func (f *LogFilter[T]) CheckKeep(req *http.Request, res *http.Response) bool {
|
||||
if len(f.Values) == 0 {
|
|
@ -4,7 +4,7 @@ import (
|
|||
"net/http"
|
||||
"testing"
|
||||
|
||||
. "github.com/yusing/go-proxy/internal/net/http/accesslog"
|
||||
. "github.com/yusing/go-proxy/internal/net/gphttp/accesslog"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||
)
|
|
@ -7,7 +7,7 @@ import (
|
|||
"strconv"
|
||||
"time"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
|
@ -19,8 +19,8 @@ type Retention struct {
|
|||
const chunkSizeMax int64 = 128 * 1024 // 128KB
|
||||
|
||||
var (
|
||||
ErrInvalidSyntax = E.New("invalid syntax")
|
||||
ErrZeroValue = E.New("zero value")
|
||||
ErrInvalidSyntax = gperr.New("invalid syntax")
|
||||
ErrZeroValue = gperr.New("zero value")
|
||||
)
|
||||
|
||||
// Syntax:
|
|
@ -4,7 +4,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/yusing/go-proxy/internal/net/http/accesslog"
|
||||
. "github.com/yusing/go-proxy/internal/net/gphttp/accesslog"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
|
@ -3,7 +3,7 @@ package accesslog
|
|||
import (
|
||||
"strconv"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/utils/strutils"
|
||||
)
|
||||
|
||||
|
@ -12,7 +12,7 @@ type StatusCodeRange struct {
|
|||
End int
|
||||
}
|
||||
|
||||
var ErrInvalidStatusCodeRange = E.New("invalid status code range")
|
||||
var ErrInvalidStatusCodeRange = gperr.New("invalid status code range")
|
||||
|
||||
func (r *StatusCodeRange) Includes(code int) bool {
|
||||
return r.Start <= code && code <= r.End
|
||||
|
@ -25,7 +25,7 @@ func (r *StatusCodeRange) Parse(v string) error {
|
|||
case 1:
|
||||
start, err := strconv.Atoi(split[0])
|
||||
if err != nil {
|
||||
return E.From(err)
|
||||
return gperr.Wrap(err)
|
||||
}
|
||||
r.Start = start
|
||||
r.End = start
|
||||
|
@ -33,7 +33,7 @@ func (r *StatusCodeRange) Parse(v string) error {
|
|||
case 2:
|
||||
start, errStart := strconv.Atoi(split[0])
|
||||
end, errEnd := strconv.Atoi(split[1])
|
||||
if err := E.Join(errStart, errEnd); err != nil {
|
||||
if err := gperr.Join(errStart, errEnd); err != nil {
|
||||
return err
|
||||
}
|
||||
r.Start = start
|
|
@ -1,7 +1,9 @@
|
|||
package utils
|
||||
package gphttp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
|
@ -10,8 +12,14 @@ import (
|
|||
|
||||
func WriteBody(w http.ResponseWriter, body []byte) {
|
||||
if _, err := w.Write(body); err != nil {
|
||||
switch {
|
||||
case errors.Is(err, http.ErrHandlerTimeout),
|
||||
errors.Is(err, context.DeadlineExceeded):
|
||||
logging.Err(err).Msg("timeout writing body")
|
||||
default:
|
||||
logging.Err(err).Msg("failed to write body")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func RespondJSON(w http.ResponseWriter, r *http.Request, data any, code ...int) (canProceed bool) {
|
||||
|
@ -25,13 +33,13 @@ func RespondJSON(w http.ResponseWriter, r *http.Request, data any, code ...int)
|
|||
case string:
|
||||
_, err = w.Write([]byte(fmt.Sprintf("%q", data)))
|
||||
case []byte:
|
||||
_, err = w.Write(data)
|
||||
panic("use WriteBody instead")
|
||||
default:
|
||||
err = json.NewEncoder(w).Encode(data)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
HandleErr(w, r, err)
|
||||
LogError(r).Err(err).Msg("failed to encode json")
|
||||
return false
|
||||
}
|
||||
return true
|
|
@ -1,4 +1,4 @@
|
|||
package http
|
||||
package gphttp
|
||||
|
||||
import (
|
||||
"mime"
|
|
@ -1,4 +1,4 @@
|
|||
package http
|
||||
package gphttp
|
||||
|
||||
import (
|
||||
"net/http"
|
|
@ -1,22 +1,21 @@
|
|||
package utils
|
||||
package gphttp
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
httpClient = &http.Client{
|
||||
Timeout: common.ConnectionTimeout,
|
||||
Timeout: 5 * time.Second,
|
||||
Transport: &http.Transport{
|
||||
DisableKeepAlives: true,
|
||||
ForceAttemptHTTP2: false,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: common.DialTimeout,
|
||||
KeepAlive: common.KeepAlive, // this is different from DisableKeepAlives
|
||||
Timeout: 3 * time.Second,
|
||||
KeepAlive: 60 * time.Second, // this is different from DisableKeepAlives
|
||||
}).DialContext,
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
},
|
95
internal/net/gphttp/error.go
Normal file
95
internal/net/gphttp/error.go
Normal file
|
@ -0,0 +1,95 @@
|
|||
package gphttp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"syscall"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
)
|
||||
|
||||
// ServerError is for handling server errors.
|
||||
//
|
||||
// It logs the error and returns http.StatusInternalServerError to the client.
|
||||
// Status code can be specified as an argument.
|
||||
func ServerError(w http.ResponseWriter, r *http.Request, err error, code ...int) {
|
||||
switch {
|
||||
case err == nil,
|
||||
errors.Is(err, context.Canceled),
|
||||
errors.Is(err, syscall.EPIPE),
|
||||
errors.Is(err, syscall.ECONNRESET):
|
||||
return
|
||||
}
|
||||
LogError(r).Msg(err.Error())
|
||||
if httpheaders.IsWebsocket(r.Header) {
|
||||
return
|
||||
}
|
||||
if len(code) == 0 {
|
||||
code = []int{http.StatusInternalServerError}
|
||||
}
|
||||
http.Error(w, http.StatusText(code[0]), code[0])
|
||||
}
|
||||
|
||||
// ClientError is for responding to client errors.
|
||||
//
|
||||
// It returns http.StatusBadRequest with reason to the client.
|
||||
// Status code can be specified as an argument.
|
||||
//
|
||||
// For JSON marshallable errors (e.g. gperr.Error), it returns the error details as JSON.
|
||||
// Otherwise, it returns the error details as plain text.
|
||||
func ClientError(w http.ResponseWriter, err error, code ...int) {
|
||||
if len(code) == 0 {
|
||||
code = []int{http.StatusBadRequest}
|
||||
}
|
||||
if gperr.IsJSONMarshallable(err) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(err)
|
||||
} else {
|
||||
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
||||
}
|
||||
http.Error(w, err.Error(), code[0])
|
||||
}
|
||||
|
||||
// JSONError returns a JSON response of gperr.Error with the given status code.
|
||||
func JSONError(w http.ResponseWriter, err gperr.Error, code int) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(err)
|
||||
http.Error(w, err.Error(), code)
|
||||
}
|
||||
|
||||
// BadRequest returns a Bad Request response with the given error message.
|
||||
func BadRequest(w http.ResponseWriter, err string, code ...int) {
|
||||
if len(code) == 0 {
|
||||
code = []int{http.StatusBadRequest}
|
||||
}
|
||||
http.Error(w, err, code[0])
|
||||
}
|
||||
|
||||
// Unauthorized returns an Unauthorized response with the given error message.
|
||||
func Unauthorized(w http.ResponseWriter, err string) {
|
||||
BadRequest(w, err, http.StatusUnauthorized)
|
||||
}
|
||||
|
||||
// NotFound returns a Not Found response with the given error message.
|
||||
func NotFound(w http.ResponseWriter, err string) {
|
||||
BadRequest(w, err, http.StatusNotFound)
|
||||
}
|
||||
|
||||
func ErrMissingKey(k string) error {
|
||||
return gperr.New(k + " is required")
|
||||
}
|
||||
|
||||
func ErrInvalidKey(k string) error {
|
||||
return gperr.New(k + " is invalid")
|
||||
}
|
||||
|
||||
func ErrAlreadyExists(k, v string) error {
|
||||
return gperr.Errorf("%s %q already exists", k, v)
|
||||
}
|
||||
|
||||
func ErrNotFound(k, v string) error {
|
||||
return gperr.Errorf("%s %q not found", k, v)
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package utils
|
||||
package gpwebsocket
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
@ -7,8 +7,10 @@ import (
|
|||
|
||||
"github.com/coder/websocket"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/net/http/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
)
|
||||
|
||||
func warnNoMatchDomains() {
|
||||
|
@ -17,7 +19,7 @@ func warnNoMatchDomains() {
|
|||
|
||||
var warnNoMatchDomainOnce sync.Once
|
||||
|
||||
func InitiateWS(w http.ResponseWriter, r *http.Request) (*websocket.Conn, error) {
|
||||
func Initiate(w http.ResponseWriter, r *http.Request) (*websocket.Conn, error) {
|
||||
var originPats []string
|
||||
|
||||
localAddresses := []string{"127.0.0.1", "10.0.*.*", "172.16.*.*", "192.168.*.*"}
|
||||
|
@ -42,17 +44,17 @@ func InitiateWS(w http.ResponseWriter, r *http.Request) (*websocket.Conn, error)
|
|||
})
|
||||
}
|
||||
|
||||
func PeriodicWS(w http.ResponseWriter, r *http.Request, interval time.Duration, do func(conn *websocket.Conn) error) {
|
||||
conn, err := InitiateWS(w, r)
|
||||
func Periodic(w http.ResponseWriter, r *http.Request, interval time.Duration, do func(conn *websocket.Conn) error) {
|
||||
conn, err := Initiate(w, r)
|
||||
if err != nil {
|
||||
HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
//nolint:errcheck
|
||||
defer conn.CloseNow()
|
||||
|
||||
if err := do(conn); err != nil {
|
||||
HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -65,9 +67,20 @@ func PeriodicWS(w http.ResponseWriter, r *http.Request, interval time.Duration,
|
|||
return
|
||||
case <-ticker.C:
|
||||
if err := do(conn); err != nil {
|
||||
HandleErr(w, r, err)
|
||||
gphttp.ServerError(w, r, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WriteText writes a text message to the websocket connection.
|
||||
// It returns true if the message was written successfully, false otherwise.
|
||||
// It logs an error if the message is not written successfully.
|
||||
func WriteText(r *http.Request, conn *websocket.Conn, msg string) bool {
|
||||
if err := conn.Write(r.Context(), websocket.MessageText, []byte(msg)); err != nil {
|
||||
gperr.LogError("failed to write text message", err)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
|
@ -17,13 +17,15 @@ const (
|
|||
HeaderXForwardedURI = "X-Forwarded-Uri"
|
||||
HeaderXRealIP = "X-Real-IP"
|
||||
|
||||
HeaderUpstreamName = "X-GoDoxy-Upstream-Name"
|
||||
HeaderUpstreamScheme = "X-GoDoxy-Upstream-Scheme"
|
||||
HeaderUpstreamHost = "X-GoDoxy-Upstream-Host"
|
||||
HeaderUpstreamPort = "X-GoDoxy-Upstream-Port"
|
||||
|
||||
HeaderContentType = "Content-Type"
|
||||
HeaderContentLength = "Content-Length"
|
||||
|
||||
HeaderUpstreamName = "X-Godoxy-Upstream-Name"
|
||||
HeaderUpstreamScheme = "X-Godoxy-Upstream-Scheme"
|
||||
HeaderUpstreamHost = "X-Godoxy-Upstream-Host"
|
||||
HeaderUpstreamPort = "X-Godoxy-Upstream-Port"
|
||||
|
||||
HeaderGoDoxyCheckRedirect = "X-Godoxy-Check-Redirect"
|
||||
)
|
||||
|
||||
// Hop-by-hop headers. These are removed when sent to the backend.
|
|
@ -6,8 +6,8 @@ import (
|
|||
"net/http"
|
||||
"sync"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/net/http/middleware"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/middleware"
|
||||
)
|
||||
|
||||
type ipHash struct {
|
||||
|
@ -23,10 +23,10 @@ func (lb *LoadBalancer) newIPHash() impl {
|
|||
if len(lb.Options) == 0 {
|
||||
return impl
|
||||
}
|
||||
var err E.Error
|
||||
var err gperr.Error
|
||||
impl.realIP, err = middleware.RealIP.New(lb.Options)
|
||||
if err != nil {
|
||||
E.LogError("invalid real_ip options, ignoring", err, &impl.l)
|
||||
gperr.LogError("invalid real_ip options, ignoring", err, &impl.l)
|
||||
}
|
||||
return impl
|
||||
}
|
|
@ -6,10 +6,10 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/net/http/loadbalancer/types"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer/types"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/internal/watcher/health"
|
||||
|
@ -54,7 +54,7 @@ func New(cfg *Config) *LoadBalancer {
|
|||
}
|
||||
|
||||
// Start implements task.TaskStarter.
|
||||
func (lb *LoadBalancer) Start(parent task.Parent) E.Error {
|
||||
func (lb *LoadBalancer) Start(parent task.Parent) gperr.Error {
|
||||
lb.startTime = time.Now()
|
||||
lb.task = parent.Subtask("loadbalancer."+lb.Link, false)
|
||||
parent.OnCancel("lb_remove_route", func() {
|
||||
|
@ -227,7 +227,7 @@ func (lb *LoadBalancer) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
|||
http.Error(rw, "Service unavailable", http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
if r.Header.Get(common.HeaderCheckRedirect) != "" {
|
||||
if r.Header.Get(httpheaders.HeaderGoDoxyCheckRedirect) != "" {
|
||||
// wake all servers
|
||||
for _, srv := range srvs {
|
||||
if err := srv.TryWake(); err != nil {
|
|
@ -3,7 +3,7 @@ package loadbalancer
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/net/http/loadbalancer/types"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer/types"
|
||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||
)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package loadbalancer
|
||||
|
||||
import (
|
||||
"github.com/yusing/go-proxy/internal/net/http/loadbalancer/types"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/loadbalancer/types"
|
||||
)
|
||||
|
||||
type (
|
|
@ -1,4 +1,4 @@
|
|||
package utils
|
||||
package gphttp
|
||||
|
||||
import (
|
||||
"net/http"
|
|
@ -1,4 +1,4 @@
|
|||
package http
|
||||
package gphttp
|
||||
|
||||
import "net/http"
|
||||
|
|
@ -5,7 +5,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
gphttp "github.com/yusing/go-proxy/internal/net/http"
|
||||
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/types"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
F "github.com/yusing/go-proxy/internal/utils/functional"
|
|
@ -7,7 +7,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
. "github.com/yusing/go-proxy/internal/utils/testing"
|
||||
)
|
||||
|
@ -61,7 +61,7 @@ func TestCIDRWhitelistValidation(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCIDRWhitelist(t *testing.T) {
|
||||
errs := E.NewBuilder("")
|
||||
errs := gperr.NewBuilder("")
|
||||
mids := BuildMiddlewaresFromYAML("", testCIDRWhitelistCompose, errs)
|
||||
ExpectNoError(t, errs.Error())
|
||||
deny = mids["deny@file"]
|
|
@ -9,9 +9,9 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
gphttp "github.com/yusing/go-proxy/internal/net/http"
|
||||
"github.com/yusing/go-proxy/internal/net/http/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/http/middleware/errorpage"
|
||||
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/httpheaders"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/middleware/errorpage"
|
||||
)
|
||||
|
||||
type customErrorPage struct{}
|
|
@ -7,7 +7,7 @@ import (
|
|||
"sync"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
E "github.com/yusing/go-proxy/internal/error"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
U "github.com/yusing/go-proxy/internal/utils"
|
||||
|
@ -90,7 +90,7 @@ func watchDir() {
|
|||
loadContent()
|
||||
}
|
||||
case err := <-errCh:
|
||||
E.LogError("error watching error page directory", err)
|
||||
gperr.LogError("error watching error page directory", err)
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue