feat: initialize managed portal
This commit is contained in:
101
internal/webdevice/proxy.go
Normal file
101
internal/webdevice/proxy.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package webdevice
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidTargetIP = errors.New("invalid target ip")
|
||||
ErrTargetNotAllowed = errors.New("target ip not allowed")
|
||||
ErrInvalidProxyURL = errors.New("invalid proxy target")
|
||||
)
|
||||
|
||||
func (s *Service) ProxyHTTP(w http.ResponseWriter, r *http.Request, targetIP, proxyPath string) error {
|
||||
if !IsPrivateIPv4Literal(targetIP) {
|
||||
return ErrInvalidTargetIP
|
||||
}
|
||||
if !s.IsAllowed(targetIP) {
|
||||
return ErrTargetNotAllowed
|
||||
}
|
||||
|
||||
rawTarget := s.ProxyTargetURL(targetIP)
|
||||
targetURL, err := url.Parse(rawTarget)
|
||||
if err != nil || targetURL.Scheme == "" || targetURL.Host == "" {
|
||||
return ErrInvalidProxyURL
|
||||
}
|
||||
|
||||
if proxyPath == "" {
|
||||
proxyPath = "/"
|
||||
}
|
||||
rawQuery := r.URL.RawQuery
|
||||
|
||||
proxy := &httputil.ReverseProxy{
|
||||
Director: func(req *http.Request) {
|
||||
req.URL.Scheme = targetURL.Scheme
|
||||
req.URL.Host = targetURL.Host
|
||||
req.URL.Path = JoinProxyTargetPath(targetURL.Path, proxyPath)
|
||||
req.URL.RawPath = ""
|
||||
req.URL.RawQuery = rawQuery
|
||||
req.Host = targetURL.Host
|
||||
req.Header.Del("Accept-Encoding")
|
||||
},
|
||||
ModifyResponse: func(resp *http.Response) error {
|
||||
proxyPrefix := "/proxy/web/" + targetIP
|
||||
if location := resp.Header.Get("Location"); location != "" {
|
||||
resp.Header.Set("Location", RewriteLocation(targetIP, targetURL, location))
|
||||
}
|
||||
if cookies := resp.Header.Values("Set-Cookie"); len(cookies) > 0 {
|
||||
resp.Header.Del("Set-Cookie")
|
||||
for _, cookie := range cookies {
|
||||
resp.Header.Add("Set-Cookie", RewriteSetCookie(cookie, proxyPrefix))
|
||||
}
|
||||
}
|
||||
|
||||
contentType := resp.Header.Get("Content-Type")
|
||||
if ShouldRewriteBody(contentType) {
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = resp.Body.Close()
|
||||
|
||||
rewritten := RewriteText(string(body), proxyPrefix, targetURL, contentType)
|
||||
rewrittenBytes := []byte(rewritten)
|
||||
resp.Body = io.NopCloser(strings.NewReader(rewritten))
|
||||
resp.ContentLength = int64(len(rewrittenBytes))
|
||||
resp.Header.Set("Content-Length", strconv.Itoa(len(rewrittenBytes)))
|
||||
resp.Header.Del("Content-Encoding")
|
||||
resp.Header.Del("Content-MD5")
|
||||
resp.Header.Del("Etag")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
||||
http.Error(w, "代理访问失败: "+err.Error(), http.StatusBadGateway)
|
||||
},
|
||||
}
|
||||
|
||||
proxy.ServeHTTP(closeNotifyWriter{ResponseWriter: w}, r)
|
||||
return nil
|
||||
}
|
||||
|
||||
type closeNotifyWriter struct {
|
||||
http.ResponseWriter
|
||||
}
|
||||
|
||||
func (w closeNotifyWriter) CloseNotify() <-chan bool {
|
||||
ch := make(chan bool, 1)
|
||||
return ch
|
||||
}
|
||||
|
||||
func (w closeNotifyWriter) Flush() {
|
||||
if flusher, ok := w.ResponseWriter.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user