mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-06 23:57:21 +02:00
Optimized upstream & loadbalancer
- Test and optimized load balancer origin picker - Fixed no active origin cannot load proxy rule bug - Implemented logger design in websocket proxy module - Added more quickstart tours - Fixed #270 (I guess) - Fixed #90 (I guess)
This commit is contained in:
parent
b558bcbfcf
commit
608cc0c523
@ -83,6 +83,10 @@ func GetUpstreamsAsString(upstreams []*Upstream) string {
|
|||||||
for _, upstream := range upstreams {
|
for _, upstream := range upstreams {
|
||||||
targets = append(targets, upstream.String())
|
targets = append(targets, upstream.String())
|
||||||
}
|
}
|
||||||
|
if len(targets) == 0 {
|
||||||
|
//No upstream
|
||||||
|
return "(no upstream config)"
|
||||||
|
}
|
||||||
return strings.Join(targets, ", ")
|
return strings.Join(targets, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +97,7 @@ func (m *RouteManager) Close() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print debug message
|
// Log Println, replace all log.Println or fmt.Println with this
|
||||||
func (m *RouteManager) debugPrint(message string, err error) {
|
func (m *RouteManager) println(message string, err error) {
|
||||||
m.Options.Logger.PrintAndLog("LoadBalancer", message, err)
|
m.Options.Logger.PrintAndLog("LoadBalancer", message, err)
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,6 @@ package loadbalance
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
@ -29,7 +27,7 @@ func (m *RouteManager) GetRequestUpstreamTarget(w http.ResponseWriter, r *http.R
|
|||||||
//No valid session found. Assign a new upstream
|
//No valid session found. Assign a new upstream
|
||||||
targetOrigin, index, err := getRandomUpstreamByWeight(origins)
|
targetOrigin, index, err := getRandomUpstreamByWeight(origins)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Oops. Unable to get random upstream")
|
m.println("Unable to get random upstream", err)
|
||||||
targetOrigin = origins[0]
|
targetOrigin = origins[0]
|
||||||
index = 0
|
index = 0
|
||||||
}
|
}
|
||||||
@ -44,7 +42,7 @@ func (m *RouteManager) GetRequestUpstreamTarget(w http.ResponseWriter, r *http.R
|
|||||||
var err error
|
var err error
|
||||||
targetOrigin, _, err = getRandomUpstreamByWeight(origins)
|
targetOrigin, _, err = getRandomUpstreamByWeight(origins)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
m.println("Failed to get next origin", err)
|
||||||
targetOrigin = origins[0]
|
targetOrigin = origins[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,6 +159,7 @@ func getRandomUpstreamByWeight(upstreams []*Upstream) (*Upstream, int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IntRange returns a random integer in the range from min to max.
|
// IntRange returns a random integer in the range from min to max.
|
||||||
|
/*
|
||||||
func intRange(min, max int) (int, error) {
|
func intRange(min, max int) (int, error) {
|
||||||
var result int
|
var result int
|
||||||
switch {
|
switch {
|
||||||
@ -175,3 +174,4 @@ func intRange(min, max int) (int, error) {
|
|||||||
}
|
}
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
@ -117,7 +117,7 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
|
|||||||
selectedUpstream, err := h.Parent.loadBalancer.GetRequestUpstreamTarget(w, r, target.ActiveOrigins, target.UseStickySession)
|
selectedUpstream, err := h.Parent.loadBalancer.GetRequestUpstreamTarget(w, r, target.ActiveOrigins, target.UseStickySession)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.ServeFile(w, r, "./web/rperror.html")
|
http.ServeFile(w, r, "./web/rperror.html")
|
||||||
log.Println(err.Error())
|
h.Parent.Option.Logger.PrintAndLog("proxy", "Failed to assign an upstream for this request", err)
|
||||||
h.Parent.logRequest(r, false, 521, "subdomain-http", r.URL.Hostname())
|
h.Parent.logRequest(r, false, 521, "subdomain-http", r.URL.Hostname())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -144,6 +144,7 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
|
|||||||
wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{
|
wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{
|
||||||
SkipTLSValidation: selectedUpstream.SkipCertValidations,
|
SkipTLSValidation: selectedUpstream.SkipCertValidations,
|
||||||
SkipOriginCheck: selectedUpstream.SkipWebSocketOriginCheck,
|
SkipOriginCheck: selectedUpstream.SkipWebSocketOriginCheck,
|
||||||
|
Logger: h.Parent.Option.Logger,
|
||||||
})
|
})
|
||||||
wspHandler.ServeHTTP(w, r)
|
wspHandler.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
@ -177,11 +178,10 @@ func (h *ProxyHandler) hostRequest(w http.ResponseWriter, r *http.Request, targe
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.As(err, &dnsError) {
|
if errors.As(err, &dnsError) {
|
||||||
http.ServeFile(w, r, "./web/hosterror.html")
|
http.ServeFile(w, r, "./web/hosterror.html")
|
||||||
log.Println(err.Error())
|
|
||||||
h.Parent.logRequest(r, false, 404, "host-http", r.URL.Hostname())
|
h.Parent.logRequest(r, false, 404, "host-http", r.URL.Hostname())
|
||||||
} else {
|
} else {
|
||||||
http.ServeFile(w, r, "./web/rperror.html")
|
http.ServeFile(w, r, "./web/rperror.html")
|
||||||
log.Println(err.Error())
|
//TODO: Take this upstream offline automatically
|
||||||
h.Parent.logRequest(r, false, 521, "host-http", r.URL.Hostname())
|
h.Parent.logRequest(r, false, 521, "host-http", r.URL.Hostname())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -212,6 +212,7 @@ func (h *ProxyHandler) vdirRequest(w http.ResponseWriter, r *http.Request, targe
|
|||||||
wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{
|
wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{
|
||||||
SkipTLSValidation: target.SkipCertValidations,
|
SkipTLSValidation: target.SkipCertValidations,
|
||||||
SkipOriginCheck: true, //You should not use websocket via virtual directory. But keep this to true for compatibility
|
SkipOriginCheck: true, //You should not use websocket via virtual directory. But keep this to true for compatibility
|
||||||
|
Logger: h.Parent.Option.Logger,
|
||||||
})
|
})
|
||||||
wspHandler.ServeHTTP(w, r)
|
wspHandler.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
|
@ -70,6 +70,11 @@ func (router *Router) PrepareProxyRoute(endpoint *ProxyEndpoint) (*ProxyEndpoint
|
|||||||
|
|
||||||
// Add Proxy Route to current runtime. Call to PrepareProxyRoute before adding to runtime
|
// Add Proxy Route to current runtime. Call to PrepareProxyRoute before adding to runtime
|
||||||
func (router *Router) AddProxyRouteToRuntime(endpoint *ProxyEndpoint) error {
|
func (router *Router) AddProxyRouteToRuntime(endpoint *ProxyEndpoint) error {
|
||||||
|
if len(endpoint.ActiveOrigins) == 0 {
|
||||||
|
//There are no active origins. No need to check for ready
|
||||||
|
router.ProxyEndpoints.Store(endpoint.RootOrMatchingDomain, endpoint)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
if !router.loadBalancer.UpstreamsReady(endpoint.ActiveOrigins) {
|
if !router.loadBalancer.UpstreamsReady(endpoint.ActiveOrigins) {
|
||||||
//This endpoint is not prepared
|
//This endpoint is not prepared
|
||||||
return errors.New("proxy endpoint not ready. Use PrepareProxyRoute before adding to runtime")
|
return errors.New("proxy endpoint not ready. Use PrepareProxyRoute before adding to runtime")
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -88,6 +88,7 @@ func (m *Manager) HandleHttpByInstanceId(instanceId string, w http.ResponseWrite
|
|||||||
wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{
|
wspHandler := websocketproxy.NewProxy(u, websocketproxy.Options{
|
||||||
SkipTLSValidation: false,
|
SkipTLSValidation: false,
|
||||||
SkipOriginCheck: false,
|
SkipOriginCheck: false,
|
||||||
|
Logger: nil,
|
||||||
})
|
})
|
||||||
wspHandler.ServeHTTP(w, r)
|
wspHandler.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
|
@ -3,6 +3,7 @@ package websocketproxy
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
@ -12,6 +13,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
"imuslab.com/zoraxy/mod/info/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -54,8 +56,9 @@ type WebsocketProxy struct {
|
|||||||
|
|
||||||
// Additional options for websocket proxy runtime
|
// Additional options for websocket proxy runtime
|
||||||
type Options struct {
|
type Options struct {
|
||||||
SkipTLSValidation bool //Skip backend TLS validation
|
SkipTLSValidation bool //Skip backend TLS validation
|
||||||
SkipOriginCheck bool //Skip origin check
|
SkipOriginCheck bool //Skip origin check
|
||||||
|
Logger *logger.Logger //Logger, can be nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProxyHandler returns a new http.Handler interface that reverse proxies the
|
// ProxyHandler returns a new http.Handler interface that reverse proxies the
|
||||||
@ -78,17 +81,26 @@ func NewProxy(target *url.URL, options Options) *WebsocketProxy {
|
|||||||
return &WebsocketProxy{Backend: backend, Verbal: false, Options: options}
|
return &WebsocketProxy{Backend: backend, Verbal: false, Options: options}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Utilities function for log printing
|
||||||
|
func (w *WebsocketProxy) Println(messsage string, err error) {
|
||||||
|
if w.Options.Logger != nil {
|
||||||
|
w.Options.Logger.PrintAndLog("websocket", messsage, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("[websocketproxy] [system:info]"+messsage, err)
|
||||||
|
}
|
||||||
|
|
||||||
// ServeHTTP implements the http.Handler that proxies WebSocket connections.
|
// ServeHTTP implements the http.Handler that proxies WebSocket connections.
|
||||||
func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
if w.Backend == nil {
|
if w.Backend == nil {
|
||||||
log.Println("websocketproxy: backend function is not defined")
|
w.Println("Invalid websocket backend configuration", errors.New("backend function not found"))
|
||||||
http.Error(rw, "internal server error (code: 1)", http.StatusInternalServerError)
|
http.Error(rw, "internal server error (code: 1)", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
backendURL := w.Backend(req)
|
backendURL := w.Backend(req)
|
||||||
if backendURL == nil {
|
if backendURL == nil {
|
||||||
log.Println("websocketproxy: backend URL is nil")
|
w.Println("Invalid websocket backend configuration", errors.New("backend URL is nil"))
|
||||||
http.Error(rw, "internal server error (code: 2)", http.StatusInternalServerError)
|
http.Error(rw, "internal server error (code: 2)", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -158,13 +170,13 @@ func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||||||
// http://tools.ietf.org/html/draft-ietf-hybi-websocket-multiplexing-01
|
// http://tools.ietf.org/html/draft-ietf-hybi-websocket-multiplexing-01
|
||||||
connBackend, resp, err := dialer.Dial(backendURL.String(), requestHeader)
|
connBackend, resp, err := dialer.Dial(backendURL.String(), requestHeader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("websocketproxy: couldn't dial to remote backend url %s", err)
|
w.Println("Couldn't dial to remote backend url "+backendURL.String(), err)
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
// If the WebSocket handshake fails, ErrBadHandshake is returned
|
// If the WebSocket handshake fails, ErrBadHandshake is returned
|
||||||
// along with a non-nil *http.Response so that callers can handle
|
// along with a non-nil *http.Response so that callers can handle
|
||||||
// redirects, authentication, etcetera.
|
// redirects, authentication, etcetera.
|
||||||
if err := copyResponse(rw, resp); err != nil {
|
if err := copyResponse(rw, resp); err != nil {
|
||||||
log.Printf("websocketproxy: couldn't write response after failed remote backend handshake: %s", err)
|
w.Println("Couldn't write response after failed remote backend handshake to "+backendURL.String(), err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
http.Error(rw, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
|
http.Error(rw, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
|
||||||
@ -198,7 +210,7 @@ func (w *WebsocketProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||||||
// Also pass the header that we gathered from the Dial handshake.
|
// Also pass the header that we gathered from the Dial handshake.
|
||||||
connPub, err := upgrader.Upgrade(rw, req, upgradeHeader)
|
connPub, err := upgrader.Upgrade(rw, req, upgradeHeader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("websocketproxy: couldn't upgrade %s", err)
|
w.Println("Couldn't upgrade incoming request", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer connPub.Close()
|
defer connPub.Close()
|
||||||
|
@ -31,6 +31,7 @@ func TestProxy(t *testing.T) {
|
|||||||
proxy := NewProxy(u, Options{
|
proxy := NewProxy(u, Options{
|
||||||
SkipTLSValidation: false,
|
SkipTLSValidation: false,
|
||||||
SkipOriginCheck: false,
|
SkipOriginCheck: false,
|
||||||
|
Logger: nil,
|
||||||
})
|
})
|
||||||
proxy.Upgrader = upgrader
|
proxy.Upgrader = upgrader
|
||||||
|
|
||||||
|
@ -910,7 +910,6 @@ func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
|
|||||||
results := []*dynamicproxy.ProxyEndpoint{}
|
results := []*dynamicproxy.ProxyEndpoint{}
|
||||||
dynamicProxyRouter.ProxyEndpoints.Range(func(key, value interface{}) bool {
|
dynamicProxyRouter.ProxyEndpoints.Range(func(key, value interface{}) bool {
|
||||||
thisEndpoint := dynamicproxy.CopyEndpoint(value.(*dynamicproxy.ProxyEndpoint))
|
thisEndpoint := dynamicproxy.CopyEndpoint(value.(*dynamicproxy.ProxyEndpoint))
|
||||||
|
|
||||||
//Clear the auth passwords before showing to front-end
|
//Clear the auth passwords before showing to front-end
|
||||||
cleanedCredentials := []*dynamicproxy.BasicAuthCredentials{}
|
cleanedCredentials := []*dynamicproxy.BasicAuthCredentials{}
|
||||||
for _, user := range thisEndpoint.BasicAuthCredentials {
|
for _, user := range thisEndpoint.BasicAuthCredentials {
|
||||||
@ -919,7 +918,6 @@ func ReverseProxyList(w http.ResponseWriter, r *http.Request) {
|
|||||||
PasswordHash: "",
|
PasswordHash: "",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
thisEndpoint.BasicAuthCredentials = cleanedCredentials
|
thisEndpoint.BasicAuthCredentials = cleanedCredentials
|
||||||
results = append(results, thisEndpoint)
|
results = append(results, thisEndpoint)
|
||||||
return true
|
return true
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
<div id="quickstart" class="standardContainer">
|
<div id="quickstart" class="standardContainer">
|
||||||
<div class="ui container">
|
<div class="ui container">
|
||||||
<h1 class="ui header">
|
<h1 class="ui header">
|
||||||
<img src="img/res/1F387.png">
|
<img src="img/res/1F44B.png">
|
||||||
<div class="content">
|
<div class="content" style="font-weight: lighter;">
|
||||||
Welcome to Zoraxy!
|
Welcome to Zoraxy!
|
||||||
<div class="sub header">What services are you planning to setup today?</div>
|
<div class="sub header">What services are you planning to setup today?</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -496,6 +496,7 @@ var tourSteps = {
|
|||||||
<br><br>Now, you can try to visit your website with https:// and see your green lock shows up next to your domain name!`,
|
<br><br>Now, you can try to visit your website with https:// and see your green lock shows up next to your domain name!`,
|
||||||
element: "#cert div[tourstep='certTable']",
|
element: "#cert div[tourstep='certTable']",
|
||||||
scrollto: "#cert div[tourstep='certTable']",
|
scrollto: "#cert div[tourstep='certTable']",
|
||||||
|
tab: "cert",
|
||||||
pos: "bottomright",
|
pos: "bottomright",
|
||||||
callback: function(){
|
callback: function(){
|
||||||
hideSideWrapperInTourMode();
|
hideSideWrapperInTourMode();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user