mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-22 16:58:54 +02:00
large refactoring, bug fixes, performance improvement
This commit is contained in:
@@ -2,20 +2,57 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
/**
|
||||
A small mod on net/http/httputil.ReverseProxy
|
||||
|
||||
Before mod:
|
||||
root@http-benchmark-client:~# wrk -t 10 -c 200 -d 10s --latency http://bench.6uo.me/bench
|
||||
Running 10s test @ http://bench.6uo.me/bench
|
||||
10 threads and 200 connections
|
||||
Thread Stats Avg Stdev Max +/- Stdev
|
||||
Latency 3.02ms 4.34ms 102.70ms 94.90%
|
||||
Req/Sec 8.06k 1.17k 9.99k 79.86%
|
||||
Latency Distribution
|
||||
50% 2.38ms
|
||||
75% 4.00ms
|
||||
90% 5.93ms
|
||||
99% 11.90ms
|
||||
808813 requests in 10.10s, 78.68MB read
|
||||
Requests/sec: 80079.47
|
||||
Transfer/sec: 7.79MB
|
||||
|
||||
After mod:
|
||||
root@http-benchmark-client:~# wrk -t 10 -c 200 -d 10s --latency http://bench.6uo.me/bench
|
||||
Running 10s test @ http://bench.6uo.me/bench
|
||||
10 threads and 200 connections
|
||||
Thread Stats Avg Stdev Max +/- Stdev
|
||||
Latency 1.77ms 5.64ms 118.14ms 99.07%
|
||||
Req/Sec 16.59k 2.22k 19.65k 87.30%
|
||||
Latency Distribution
|
||||
50% 1.11ms
|
||||
75% 1.85ms
|
||||
90% 2.74ms
|
||||
99% 6.68ms
|
||||
1665286 requests in 10.10s, 200.11MB read
|
||||
Requests/sec: 164880.11
|
||||
Transfer/sec: 19.81MB
|
||||
**/
|
||||
type HTTPRoute struct {
|
||||
Alias string
|
||||
Url *url.URL
|
||||
Path string
|
||||
PathMode string
|
||||
Proxy *httputil.ReverseProxy
|
||||
Proxy *ReverseProxy
|
||||
|
||||
l logrus.FieldLogger
|
||||
}
|
||||
|
||||
func NewHTTPRoute(config *ProxyConfig) (*HTTPRoute, error) {
|
||||
@@ -24,8 +61,14 @@ func NewHTTPRoute(config *ProxyConfig) (*HTTPRoute, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proxy := httputil.NewSingleHostReverseProxy(url)
|
||||
proxy.Transport = transport
|
||||
var tr *http.Transport
|
||||
if config.NoTLSVerify {
|
||||
tr = transportNoTLS
|
||||
} else {
|
||||
tr = transport
|
||||
}
|
||||
|
||||
proxy := NewSingleHostReverseProxy(url, tr)
|
||||
|
||||
if !isValidProxyPathMode(config.PathMode) {
|
||||
return nil, fmt.Errorf("invalid path mode: %s", config.PathMode)
|
||||
@@ -37,22 +80,21 @@ func NewHTTPRoute(config *ProxyConfig) (*HTTPRoute, error) {
|
||||
Path: config.Path,
|
||||
Proxy: proxy,
|
||||
PathMode: config.PathMode,
|
||||
l: hrlog.WithFields(logrus.Fields{
|
||||
"alias": config.Alias,
|
||||
"path": config.Path,
|
||||
"path_mode": config.PathMode,
|
||||
}),
|
||||
}
|
||||
|
||||
director := proxy.Director
|
||||
proxy.Director = nil
|
||||
|
||||
initRewrite := func(pr *httputil.ProxyRequest) {
|
||||
director(pr.Out)
|
||||
}
|
||||
rewrite := initRewrite
|
||||
var rewrite func(*ProxyRequest)
|
||||
|
||||
switch {
|
||||
case config.Path == "", config.PathMode == ProxyPathMode_Forward:
|
||||
break
|
||||
rewrite = proxy.Rewrite
|
||||
case config.PathMode == ProxyPathMode_Sub:
|
||||
rewrite = func(pr *httputil.ProxyRequest) {
|
||||
initRewrite(pr)
|
||||
rewrite = func(pr *ProxyRequest) {
|
||||
proxy.Rewrite(pr)
|
||||
// disable compression
|
||||
pr.Out.Header.Set("Accept-Encoding", "identity")
|
||||
// remove path prefix
|
||||
@@ -61,9 +103,7 @@ func NewHTTPRoute(config *ProxyConfig) (*HTTPRoute, error) {
|
||||
route.Proxy.ModifyResponse = func(r *http.Response) error {
|
||||
contentType, ok := r.Header["Content-Type"]
|
||||
if !ok || len(contentType) == 0 {
|
||||
if glog.V(3) {
|
||||
glog.Infof("[Path sub] unknown content type for %s", r.Request.URL.String())
|
||||
}
|
||||
route.l.Debug("unknown content type for", r.Request.URL.String())
|
||||
return nil
|
||||
}
|
||||
// disable cache
|
||||
@@ -76,28 +116,27 @@ func NewHTTPRoute(config *ProxyConfig) (*HTTPRoute, error) {
|
||||
case strings.HasPrefix(contentType[0], "application/javascript"):
|
||||
err = utils.respJSSubPath(r, config.Path)
|
||||
default:
|
||||
glog.V(4).Infof("[Path sub] unknown content type(s): %s", contentType)
|
||||
route.l.Debug("unknown content type(s): ", contentType)
|
||||
}
|
||||
if err != nil {
|
||||
err = fmt.Errorf("[Path sub] failed to remove path prefix %s: %v", config.Path, err)
|
||||
err = fmt.Errorf("failed to remove path prefix %s: %v", config.Path, err)
|
||||
route.l.WithField("action", "path_sub").Error(err)
|
||||
r.Status = err.Error()
|
||||
r.StatusCode = http.StatusInternalServerError
|
||||
}
|
||||
return err
|
||||
}
|
||||
default:
|
||||
rewrite = func(pr *httputil.ProxyRequest) {
|
||||
initRewrite(pr)
|
||||
rewrite = func(pr *ProxyRequest) {
|
||||
proxy.Rewrite(pr)
|
||||
pr.Out.URL.Path = strings.TrimPrefix(pr.Out.URL.Path, config.Path)
|
||||
}
|
||||
}
|
||||
|
||||
if glog.V(3) {
|
||||
route.Proxy.Rewrite = func(pr *httputil.ProxyRequest) {
|
||||
if logLevel == logrus.DebugLevel {
|
||||
route.Proxy.Rewrite = func(pr *ProxyRequest) {
|
||||
rewrite(pr)
|
||||
r := pr.In
|
||||
glog.Infof("[Request] %s %s%s", r.Method, r.Host, r.URL.Path)
|
||||
glog.V(5).InfoDepthf(1, "Headers: %v", r.Header)
|
||||
route.l.Debug("Request headers: ", pr.In.Header)
|
||||
}
|
||||
} else {
|
||||
route.Proxy.Rewrite = rewrite
|
||||
@@ -114,7 +153,7 @@ func (p *httpLoadBalancePool) Pick() *HTTPRoute {
|
||||
}
|
||||
|
||||
func (r *HTTPRoute) RemoveFromRoutes() {
|
||||
routes.HTTPRoutes.Delete(r.Alias)
|
||||
httpRoutes.Delete(r.Alias)
|
||||
}
|
||||
|
||||
// dummy implementation for Route interface
|
||||
@@ -144,24 +183,30 @@ func redirectToTLS(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
func findHTTPRoute(host string, path string) (*HTTPRoute, error) {
|
||||
subdomain := strings.Split(host, ".")[0]
|
||||
routeMap, ok := routes.HTTPRoutes.UnsafeGet(subdomain)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("no matching route for subdomain %s", subdomain)
|
||||
routeMap, ok := httpRoutes.UnsafeGet(subdomain)
|
||||
if ok {
|
||||
return routeMap.FindMatch(path)
|
||||
}
|
||||
return routeMap.FindMatch(path)
|
||||
return nil, fmt.Errorf("no matching route for subdomain %s", subdomain)
|
||||
}
|
||||
|
||||
func httpProxyHandler(w http.ResponseWriter, r *http.Request) {
|
||||
route, err := findHTTPRoute(r.Host, r.URL.Path)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("[Request] failed %s %s%s, error: %v",
|
||||
err = fmt.Errorf("request failed %s %s%s, error: %v",
|
||||
r.Method,
|
||||
r.Host,
|
||||
r.URL.Path,
|
||||
err,
|
||||
)
|
||||
logrus.Error(err)
|
||||
http.Error(w, err.Error(), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
route.Proxy.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// alias -> (path -> routes)
|
||||
type HTTPRoutes = SafeMap[string, *pathPoolMap]
|
||||
|
||||
var httpRoutes HTTPRoutes = NewSafeMap[string](newPathPoolMap)
|
||||
|
||||
Reference in New Issue
Block a user