初始化蓝鸟文件快递柜源码与部署文档

This commit is contained in:
lhc committed 2026-10-05 23:41:07 +08:00
commit 5643d22242
70 files changed
+18669

No files matched your search

+172
View File
@@ -0,0 +1,172 @@
package app
import (
"context"
"errors"
"net"
"net/http"
"net/url"
"strconv"
"strings"
)
func normalizeAccessURL(raw string) (string, error) {
u, err := url.Parse(raw)
if err != nil || u.Host == "" || u.Hostname() == "" || u.User != nil || u.RawQuery != "" || u.Fragment != "" || (u.Path != "" && u.Path != "/") || (u.Scheme != "http" && u.Scheme != "https") {
return "", errors.New("访问地址须为无路径的http/https站点地址")
}
host := strings.ToLower(u.Hostname())
port := u.Port()
if port != "" {
n, e := strconv.Atoi(port)
if e != nil || n < 1 || n > 65535 {
return "", errors.New("访问端口须为1至65535")
}
}
if port == "80" && u.Scheme == "http" || port == "443" && u.Scheme == "https" {
port = ""
}
if port != "" {
host = net.JoinHostPort(host, port)
} else if strings.Contains(host, ":") {
host = "[" + host + "]"
}
return u.Scheme + "://" + host, nil
}
func (c Config) accessOrigins() []string {
out := []string{}
for _, raw := range append([]string{c.PublicURL}, c.AccessURLs...) {
base, err := normalizeAccessURL(raw)
if err != nil {
continue
}
found := false
for _, v := range out {
if v == base {
found = true
break
}
}
if !found {
out = append(out, base)
}
}
return out
}
type requestAccessKey struct{}
type requestAccess struct {
base string
httpCookie bool
}
func (a *App) accessFor(r *http.Request) requestAccess {
if state, ok := r.Context().Value(requestAccessKey{}).(requestAccess); ok {
return state
}
return requestAccess{base: strings.TrimRight(a.cfg.PublicURL, "/")}
}
func (a *App) selectAccess(r *http.Request) (requestAccess, error) {
origins := a.cfg.accessOrigins()
allowed := func(raw string) string {
normalized, err := normalizeAccessURL(raw)
if err != nil {
return ""
}
for _, v := range origins {
if v == normalized {
return v
}
}
return ""
}
origin, hint := r.Header.Get("Origin"), r.Header.Get("X-Bluebird-Origin")
selected := ""
if origin != "" {
selected = allowed(origin)
if selected == "" {
return requestAccess{}, errors.New("请求来源不允许")
}
}
if hint != "" {
base := allowed(hint)
if base == "" || selected != "" && base != selected {
return requestAccess{}, errors.New("访问入口不允许")
}
selected = base
}
if selected == "" {
host, scheme := r.Host, ""
if r.TLS != nil {
scheme = "https"
}
peer, _, err := net.SplitHostPort(r.RemoteAddr)
ip := net.ParseIP(peer)
trusted := false
if err == nil {
for _, network := range a.proxies {
if network.Contains(ip) {
trusted = true
break
}
}
}
if trusted {
if forwarded := r.Header.Get("X-Forwarded-Host"); forwarded != "" && !strings.Contains(forwarded, ",") {
host = forwarded
}
if proto := r.Header.Get("X-Forwarded-Proto"); proto == "http" || proto == "https" {
scheme = proto
}
}
for _, base := range origins {
u, _ := url.Parse(base)
if strings.EqualFold(u.Host, host) && (scheme == "" || scheme == u.Scheme) {
selected = base
break
}
}
}
if selected == "" {
selected = origins[0]
}
if r.TLS != nil && strings.HasPrefix(selected, "http://") {
return requestAccess{}, errors.New("访问入口协议与连接不一致")
}
hasHTTPS := false
for _, base := range origins {
if strings.HasPrefix(base, "https://") {
hasHTTPS = true
break
}
}
return requestAccess{base: selected, httpCookie: hasHTTPS && strings.HasPrefix(selected, "http://")}, nil
}
func (a *App) withAccess(r *http.Request) (*http.Request, error) {
state, err := a.selectAccess(r)
if err != nil {
return nil, err
}
return r.WithContext(context.WithValue(r.Context(), requestAccessKey{}, state)), nil
}
func cookieNames(r *http.Request, name string) []string {
state, _ := r.Context().Value(requestAccessKey{}).(requestAccess)
if state.httpCookie && (r.Header.Get("Origin") != "" || r.Header.Get("X-Bluebird-Origin") != "") {
return []string{name + "_http", name}
}
return []string{name, name + "_http"}
}
// LANListen accepts only a concrete private or loopback HTTP address.
func LANListen(raw string) (string, string, error) {
base, err := normalizeAccessURL(raw)
if err != nil {
return "", "", err
}
u, _ := url.Parse(base)
ip := net.ParseIP(u.Hostname())
if u.Scheme != "http" || ip == nil || !ip.IsPrivate() && !ip.IsLoopback() || u.Port() == "" {
return "", "", errors.New("内网入口须为http://具体私有IP:端口")
}
return u.Host, base, nil
}