mirror of
https://github.com/yusing/godoxy.git
synced 2026-01-15 14:53:35 +01:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77e486f4fe | ||
|
|
3ccaba3163 | ||
|
|
705923960c | ||
|
|
ca737c8979 | ||
|
|
b6b5d4dbd7 | ||
|
|
b2919fbaf6 | ||
|
|
722c40d103 |
@@ -22,6 +22,8 @@ services:
|
||||
- ${SOCKET_PROXY_LISTEN_ADDR:-127.0.0.1:2375}:2375
|
||||
frontend:
|
||||
image: ghcr.io/yusing/godoxy-frontend:${TAG:-latest}
|
||||
# lite variant
|
||||
# image: ghcr.io/yusing/godoxy-frontend:${TAG:-latest}-lite
|
||||
container_name: godoxy-frontend
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
@@ -74,10 +76,9 @@ services:
|
||||
- ./error_pages:/app/error_pages:ro
|
||||
- ./data:/app/data
|
||||
|
||||
# To use autocert, certs will be stored in "./certs".
|
||||
# You can also use a docker volume to store it
|
||||
# This path stores certs obtained from autocert and agent TLS client certs
|
||||
- ./certs:/app/certs
|
||||
|
||||
# remove "./certs:/app/certs" and uncomment below to use existing certificate
|
||||
# mount existing certificate
|
||||
# - /path/to/certs/cert.crt:/app/certs/cert.crt
|
||||
# - /path/to/certs/priv.key:/app/certs/priv.key
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
# autocert:
|
||||
# provider: local
|
||||
# cert_path: /path/to/cert.crt # default: /app/certs/cert.crt
|
||||
# key_path: /path/to/priv.key # default: /app/certs/priv.key
|
||||
|
||||
# 2. cloudflare
|
||||
# autocert:
|
||||
|
||||
@@ -51,6 +51,10 @@ func ProceedNext(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func AuthCheckHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if defaultAuth == nil {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
return
|
||||
}
|
||||
err := defaultAuth.CheckToken(r)
|
||||
if err != nil {
|
||||
defaultAuth.LoginHandler(w, r)
|
||||
@@ -60,6 +64,10 @@ func AuthCheckHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func AuthOrProceed(w http.ResponseWriter, r *http.Request) (proceed bool) {
|
||||
if defaultAuth == nil {
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
return false
|
||||
}
|
||||
err := defaultAuth.CheckToken(r)
|
||||
if err != nil {
|
||||
defaultAuth.LoginHandler(w, r)
|
||||
|
||||
@@ -47,9 +47,10 @@ func (m *modifyHTML) modifyResponse(resp *http.Response) error {
|
||||
|
||||
// Skip modification for streaming/chunked responses to avoid blocking reads
|
||||
// Unknown content length or any transfer encoding indicates streaming.
|
||||
if resp.ContentLength < 0 || len(resp.TransferEncoding) > 0 {
|
||||
return nil
|
||||
}
|
||||
// if resp.ContentLength < 0 || len(resp.TransferEncoding) > 0 {
|
||||
// log.Debug().Str("url", fullURL(resp.Request)).Strs("transfer-encoding", resp.TransferEncoding).Msg("skipping modification for streaming/chunked response")
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// NOTE: do not put it in the defer, it will be used as resp.Body
|
||||
content, release, err := httputils.ReadAllBody(resp)
|
||||
|
||||
@@ -86,6 +86,10 @@ func (s *FileServer) Start(parent task.Parent) gperr.Error {
|
||||
}
|
||||
}
|
||||
|
||||
if len(s.Rules) > 0 {
|
||||
s.handler = s.Rules.BuildHandler(s.handler.ServeHTTP)
|
||||
}
|
||||
|
||||
if s.UseHealthCheck() {
|
||||
s.HealthMon = monitor.NewFileServerHealthMonitor(s.HealthCheck, s.Root)
|
||||
if err := s.HealthMon.Start(s.task); err != nil {
|
||||
|
||||
@@ -82,19 +82,39 @@ type (
|
||||
impl types.Route
|
||||
task *task.Task
|
||||
|
||||
isValidated bool
|
||||
lastError gperr.Error
|
||||
provider types.RouteProvider
|
||||
// ensure err is read after validation or start
|
||||
valErr lockedError
|
||||
startErr lockedError
|
||||
|
||||
provider types.RouteProvider
|
||||
|
||||
agent *agent.AgentConfig
|
||||
|
||||
started chan struct{}
|
||||
once sync.Once
|
||||
started chan struct{}
|
||||
onceStart sync.Once
|
||||
onceValidate sync.Once
|
||||
}
|
||||
Routes map[string]*Route
|
||||
Port = route.Port
|
||||
)
|
||||
|
||||
type lockedError struct {
|
||||
err gperr.Error
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func (le *lockedError) Get() gperr.Error {
|
||||
le.lock.Lock()
|
||||
defer le.lock.Unlock()
|
||||
return le.err
|
||||
}
|
||||
|
||||
func (le *lockedError) Set(err gperr.Error) {
|
||||
le.lock.Lock()
|
||||
defer le.lock.Unlock()
|
||||
le.err = err
|
||||
}
|
||||
|
||||
const DefaultHost = "localhost"
|
||||
|
||||
func (r Routes) Contains(alias string) bool {
|
||||
@@ -103,11 +123,13 @@ func (r Routes) Contains(alias string) bool {
|
||||
}
|
||||
|
||||
func (r *Route) Validate() gperr.Error {
|
||||
if r.isValidated {
|
||||
return r.lastError
|
||||
}
|
||||
r.isValidated = true
|
||||
r.onceValidate.Do(func() {
|
||||
r.valErr.Set(r.validate())
|
||||
})
|
||||
return r.valErr.Get()
|
||||
}
|
||||
|
||||
func (r *Route) validate() gperr.Error {
|
||||
if r.Agent != "" {
|
||||
if r.Container != nil {
|
||||
return gperr.Errorf("specifying agent is not allowed for docker container routes")
|
||||
@@ -250,7 +272,6 @@ func (r *Route) Validate() gperr.Error {
|
||||
}
|
||||
|
||||
if errs.HasError() {
|
||||
r.lastError = errs.Error()
|
||||
return errs.Error()
|
||||
}
|
||||
|
||||
@@ -266,7 +287,6 @@ func (r *Route) Validate() gperr.Error {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
r.lastError = err
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -320,13 +340,10 @@ func (r *Route) Task() *task.Task {
|
||||
}
|
||||
|
||||
func (r *Route) Start(parent task.Parent) gperr.Error {
|
||||
if r.lastError != nil {
|
||||
return r.lastError
|
||||
}
|
||||
r.once.Do(func() {
|
||||
r.lastError = r.start(parent)
|
||||
r.onceStart.Do(func() {
|
||||
r.startErr.Set(r.start(parent))
|
||||
})
|
||||
return r.lastError
|
||||
return r.startErr.Get()
|
||||
}
|
||||
|
||||
func (r *Route) start(parent task.Parent) gperr.Error {
|
||||
@@ -496,7 +513,7 @@ func (r *Route) IsZeroPort() bool {
|
||||
}
|
||||
|
||||
func (r *Route) ShouldExclude() bool {
|
||||
if r.lastError != nil {
|
||||
if r.valErr.Get() != nil {
|
||||
return true
|
||||
}
|
||||
if r.Excluded {
|
||||
@@ -565,7 +582,7 @@ func (re ExcludedReason) MarshalJSON() ([]byte, error) {
|
||||
// no need to unmarshal json because we don't store this
|
||||
|
||||
func (r *Route) findExcludedReason() ExcludedReason {
|
||||
if r.lastError != nil {
|
||||
if r.valErr.Get() != nil {
|
||||
return ExcludedReasonError
|
||||
}
|
||||
if r.ExcludedReason != ExcludedReasonNone {
|
||||
|
||||
@@ -6,7 +6,11 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/bytedance/sonic"
|
||||
"github.com/quic-go/quic-go/http3"
|
||||
"github.com/rs/zerolog/log"
|
||||
"golang.org/x/net/http2"
|
||||
|
||||
_ "unsafe"
|
||||
)
|
||||
|
||||
type (
|
||||
@@ -263,6 +267,30 @@ func (rule *Rule) Handle(w http.ResponseWriter, r *http.Request) error {
|
||||
return rule.Do.exec.Handle(w, r)
|
||||
}
|
||||
|
||||
//go:linkname errStreamClosed golang.org/x/net/http2.errStreamClosed
|
||||
var errStreamClosed error
|
||||
|
||||
func logError(err error, r *http.Request) {
|
||||
if errors.Is(err, errStreamClosed) {
|
||||
return
|
||||
}
|
||||
var h2Err http2.StreamError
|
||||
if errors.As(err, &h2Err) {
|
||||
// ignore these errors
|
||||
switch h2Err.Code {
|
||||
case http2.ErrCodeStreamClosed:
|
||||
return
|
||||
}
|
||||
}
|
||||
var h3Err *http3.Error
|
||||
if errors.As(err, &h3Err) {
|
||||
// ignore these errors
|
||||
switch h3Err.ErrorCode {
|
||||
case
|
||||
http3.ErrCodeNoError,
|
||||
http3.ErrCodeRequestCanceled:
|
||||
return
|
||||
}
|
||||
}
|
||||
log.Err(err).Str("method", r.Method).Str("url", r.Host+r.URL.Path).Msg("error executing rules")
|
||||
}
|
||||
|
||||
@@ -41,12 +41,17 @@ func ValidateWithCustomValidator(v reflect.Value) gperr.Error {
|
||||
} else {
|
||||
vt := v.Type()
|
||||
if vt.PkgPath() != "" { // not a builtin type
|
||||
// prioritize pointer method
|
||||
if v.CanAddr() {
|
||||
vAddr := v.Addr()
|
||||
if vAddr.Type().Implements(validatorType) {
|
||||
return vAddr.Interface().(CustomValidator).Validate()
|
||||
}
|
||||
}
|
||||
// fallback to value method
|
||||
if vt.Implements(validatorType) {
|
||||
return v.Interface().(CustomValidator).Validate()
|
||||
}
|
||||
if v.CanAddr() {
|
||||
return validateWithValidator(v.Addr())
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user