add header checker
Some checks failed
build container / build-container (push) Successful in 33m30s
run go test / test (push) Failing after 3s

This commit is contained in:
guochao 2025-04-01 14:09:43 +08:00
parent 85968bb5cf
commit 835045346d
3 changed files with 60 additions and 9 deletions

View File

@ -15,9 +15,20 @@ type PathTransformation struct {
Replace string `yaml:"replace"` Replace string `yaml:"replace"`
} }
type HeaderChecker struct {
Name string `yaml:"name"`
Match *string `yaml:"match"`
}
type Checker struct {
StatusCodes []int `yaml:"status-codes"`
Headers []HeaderChecker `yaml:"headers"`
}
type Upstream struct { type Upstream struct {
Server string `yaml:"server"` Server string `yaml:"server"`
Path PathTransformation `yaml:"path"` Path PathTransformation `yaml:"path"`
Checkers []Checker `yaml:"checkers"`
AllowedRedirect *string `yaml:"allowed-redirect"` AllowedRedirect *string `yaml:"allowed-redirect"`
PriorityGroups []UpstreamPriorityGroup `yaml:"priority-groups"` PriorityGroups []UpstreamPriorityGroup `yaml:"priority-groups"`
} }

View File

@ -11,6 +11,10 @@ upstream:
path: path:
match: /(debian|ubuntu|ubuntu-releases|alpine|archlinux|kali|manjaro|msys2|almalinux|rocky|centos|centos-stream|centos-vault|fedora|epel|elrepo|remi|rpmfusion|tailscale|gnu|rust-static|pypi)/(.*) match: /(debian|ubuntu|ubuntu-releases|alpine|archlinux|kali|manjaro|msys2|almalinux|rocky|centos|centos-stream|centos-vault|fedora|epel|elrepo|remi|rpmfusion|tailscale|gnu|rust-static|pypi)/(.*)
replace: '/$1/$2' replace: '/$1/$2'
checkers:
headers:
- name: content-type
match: application/octet-stream
- server: https://packages.microsoft.com/repos/code - server: https://packages.microsoft.com/repos/code
path: path:
match: /microsoft-code(.*) match: /microsoft-code(.*)

View File

@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"context" "context"
"errors" "errors"
"fmt"
"io" "io"
"log/slog" "log/slog"
"net" "net"
@ -618,18 +617,54 @@ func (server *Server) tryUpstream(ctx context.Context, upstreamIdx, priority int
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
streaming := false
defer func() {
if !streaming {
response.Body.Close()
}
}()
if response.StatusCode == http.StatusNotModified { if response.StatusCode == http.StatusNotModified {
return response, nil, nil return response, nil, nil
} }
if response.StatusCode >= 400 && response.StatusCode < 500 {
responseCheckers := upstream.Checkers
if len(responseCheckers) == 0 {
responseCheckers = append(responseCheckers, Checker{})
}
for _, checker := range responseCheckers {
if len(checker.StatusCodes) == 0 {
checker.StatusCodes = append(checker.StatusCodes, http.StatusOK)
}
if !slices.Contains(checker.StatusCodes, response.StatusCode) {
return nil, nil, err
}
for _, headerChecker := range checker.Headers {
if headerChecker.Match == nil {
// check header exists
if _, ok := response.Header[headerChecker.Name]; !ok {
logger.Debug("missing header", "header", headerChecker.Name)
return nil, nil, nil return nil, nil, nil
} }
if response.StatusCode < 200 || response.StatusCode >= 500 { } else {
logger.With( // check header match
"url", newurl, value := response.Header.Get(headerChecker.Name)
"status", response.StatusCode, if matched, err := regexp.MatchString(*headerChecker.Match, value); err != nil {
).Warn("unexpected status") return nil, nil, err
return response, nil, fmt.Errorf("unexpected status(url=%v): %v: %v", newurl, response.StatusCode, response) } else if !matched {
logger.Debug("invalid header value",
"header", headerChecker.Name,
"value", value,
"matcher", *headerChecker.Match,
)
return nil, nil, nil
}
}
}
} }
var currentOffset int64 var currentOffset int64
@ -646,6 +681,7 @@ func (server *Server) tryUpstream(ctx context.Context, upstreamIdx, priority int
} }
ch <- Chunk{buffer: buffer[:n]} ch <- Chunk{buffer: buffer[:n]}
streaming = true
go func() { go func() {
defer close(ch) defer close(ch)