mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-06-06 07:37:21 +02:00
Exposed dpcore timeout options
- Exposed idle timeout and response timeout option - Updated upstream edit UI to use the new API - Updated geodb
This commit is contained in:
parent
36c1f149e6
commit
caf4ab331b
@ -83,9 +83,12 @@ type requestCanceler interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DpcoreOptions struct {
|
type DpcoreOptions struct {
|
||||||
IgnoreTLSVerification bool //Disable all TLS verification when request pass through this proxy router
|
IgnoreTLSVerification bool //Disable all TLS verification when request pass through this proxy router
|
||||||
FlushInterval time.Duration //Duration to flush in normal requests. Stream request or keep-alive request will always flush with interval of -1 (immediately)
|
FlushInterval time.Duration //Duration to flush in normal requests. Stream request or keep-alive request will always flush with interval of -1 (immediately)
|
||||||
UseH2CRoundTripper bool //Use H2C RoundTripper for HTTP/2.0 connection
|
MaxConcurrentConnection int //Maxmium concurrent requests to this server
|
||||||
|
ResponseHeaderTimeout int64 //Timeout for response header, set to 0 for default
|
||||||
|
IdleConnectionTimeout int64 //Idle connection timeout, set to 0 for default
|
||||||
|
UseH2CRoundTripper bool //Use H2C RoundTripper for HTTP/2.0 connection
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDynamicProxyCore(target *url.URL, prepender string, dpcOptions *DpcoreOptions) *ReverseProxy {
|
func NewDynamicProxyCore(target *url.URL, prepender string, dpcOptions *DpcoreOptions) *ReverseProxy {
|
||||||
@ -106,18 +109,30 @@ func NewDynamicProxyCore(target *url.URL, prepender string, dpcOptions *DpcoreOp
|
|||||||
|
|
||||||
//Hack the default transporter to handle more connections
|
//Hack the default transporter to handle more connections
|
||||||
optimalConcurrentConnection := 32
|
optimalConcurrentConnection := 32
|
||||||
|
if dpcOptions.MaxConcurrentConnection > 0 {
|
||||||
|
optimalConcurrentConnection = dpcOptions.MaxConcurrentConnection
|
||||||
|
}
|
||||||
|
thisTransporter.(*http.Transport).IdleConnTimeout = 30 * time.Second
|
||||||
thisTransporter.(*http.Transport).MaxIdleConns = optimalConcurrentConnection * 2
|
thisTransporter.(*http.Transport).MaxIdleConns = optimalConcurrentConnection * 2
|
||||||
thisTransporter.(*http.Transport).MaxIdleConnsPerHost = optimalConcurrentConnection
|
thisTransporter.(*http.Transport).MaxIdleConnsPerHost = optimalConcurrentConnection
|
||||||
thisTransporter.(*http.Transport).IdleConnTimeout = 30 * time.Second
|
|
||||||
thisTransporter.(*http.Transport).MaxConnsPerHost = optimalConcurrentConnection * 2
|
thisTransporter.(*http.Transport).MaxConnsPerHost = optimalConcurrentConnection * 2
|
||||||
thisTransporter.(*http.Transport).DisableCompression = true
|
thisTransporter.(*http.Transport).DisableCompression = true
|
||||||
|
|
||||||
|
if dpcOptions.ResponseHeaderTimeout > 0 {
|
||||||
|
//Set response header timeout
|
||||||
|
thisTransporter.(*http.Transport).ResponseHeaderTimeout = time.Duration(dpcOptions.ResponseHeaderTimeout) * time.Millisecond
|
||||||
|
}
|
||||||
|
|
||||||
|
if dpcOptions.IdleConnectionTimeout > 0 {
|
||||||
|
//Set idle connection timeout
|
||||||
|
thisTransporter.(*http.Transport).IdleConnTimeout = time.Duration(dpcOptions.IdleConnectionTimeout) * time.Millisecond
|
||||||
|
}
|
||||||
|
|
||||||
if dpcOptions.IgnoreTLSVerification {
|
if dpcOptions.IgnoreTLSVerification {
|
||||||
//Ignore TLS certificate validation error
|
//Ignore TLS certificate validation error
|
||||||
thisTransporter.(*http.Transport).TLSClientConfig.InsecureSkipVerify = true
|
thisTransporter.(*http.Transport).TLSClientConfig.InsecureSkipVerify = true
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: Add user adjustable timeout option here
|
|
||||||
if dpcOptions.UseH2CRoundTripper {
|
if dpcOptions.UseH2CRoundTripper {
|
||||||
//Use H2C RoundTripper for HTTP/2.0 connection
|
//Use H2C RoundTripper for HTTP/2.0 connection
|
||||||
thisTransporter = modh2c.NewH2CRoundTripper()
|
thisTransporter = modh2c.NewH2CRoundTripper()
|
||||||
|
@ -43,8 +43,12 @@ type Upstream struct {
|
|||||||
SkipWebSocketOriginCheck bool //Skip origin check on websocket upgrade connections
|
SkipWebSocketOriginCheck bool //Skip origin check on websocket upgrade connections
|
||||||
|
|
||||||
//Load balancing configs
|
//Load balancing configs
|
||||||
Weight int //Random weight for round robin, 0 for fallback only
|
Weight int //Random weight for round robin, 0 for fallback only
|
||||||
MaxConn int //TODO: Maxmium connection to this server, 0 for unlimited
|
|
||||||
|
//HTTP Transport Config
|
||||||
|
MaxConn int //Maxmium concurrent requests to this upstream dpcore instance
|
||||||
|
RespTimeout int64 //Response header timeout in milliseconds
|
||||||
|
IdleTimeout int64 //Idle connection timeout in milliseconds
|
||||||
|
|
||||||
//currentConnectionCounts atomic.Uint64 //Counter for number of client currently connected
|
//currentConnectionCounts atomic.Uint64 //Counter for number of client currently connected
|
||||||
proxy *dpcore.ReverseProxy
|
proxy *dpcore.ReverseProxy
|
||||||
|
@ -39,8 +39,11 @@ func (u *Upstream) StartProxy() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
proxy := dpcore.NewDynamicProxyCore(path, "", &dpcore.DpcoreOptions{
|
proxy := dpcore.NewDynamicProxyCore(path, "", &dpcore.DpcoreOptions{
|
||||||
IgnoreTLSVerification: u.SkipCertValidations,
|
IgnoreTLSVerification: u.SkipCertValidations,
|
||||||
FlushInterval: 100 * time.Millisecond,
|
FlushInterval: 100 * time.Millisecond,
|
||||||
|
ResponseHeaderTimeout: u.RespTimeout,
|
||||||
|
IdleConnectionTimeout: u.IdleTimeout,
|
||||||
|
MaxConcurrentConnection: u.MaxConn,
|
||||||
})
|
})
|
||||||
|
|
||||||
u.proxy = proxy
|
u.proxy = proxy
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -79,6 +79,25 @@ func ReverseProxyUpstreamAdd(w http.ResponseWriter, r *http.Request) {
|
|||||||
utils.SendErrorResponse(w, "upstream origin not set")
|
utils.SendErrorResponse(w, "upstream origin not set")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Response timeout in seconds, set to 0 for default
|
||||||
|
respTimeout, err := utils.PostInt(r, "respt")
|
||||||
|
if err != nil {
|
||||||
|
respTimeout = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
//Idle timeout in seconds, set to 0 for default
|
||||||
|
idleTimeout, err := utils.PostInt(r, "idlet")
|
||||||
|
if err != nil {
|
||||||
|
idleTimeout = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
//Max concurrent connection to dpcore instance, set to 0 for default
|
||||||
|
maxConn, err := utils.PostInt(r, "maxconn")
|
||||||
|
if err != nil {
|
||||||
|
maxConn = 0
|
||||||
|
}
|
||||||
|
|
||||||
requireTLS, _ := utils.PostBool(r, "tls")
|
requireTLS, _ := utils.PostBool(r, "tls")
|
||||||
skipTlsValidation, _ := utils.PostBool(r, "tlsval")
|
skipTlsValidation, _ := utils.PostBool(r, "tlsval")
|
||||||
bpwsorg, _ := utils.PostBool(r, "bpwsorg")
|
bpwsorg, _ := utils.PostBool(r, "bpwsorg")
|
||||||
@ -91,7 +110,9 @@ func ReverseProxyUpstreamAdd(w http.ResponseWriter, r *http.Request) {
|
|||||||
SkipCertValidations: skipTlsValidation,
|
SkipCertValidations: skipTlsValidation,
|
||||||
SkipWebSocketOriginCheck: bpwsorg,
|
SkipWebSocketOriginCheck: bpwsorg,
|
||||||
Weight: 1,
|
Weight: 1,
|
||||||
MaxConn: 0,
|
MaxConn: maxConn,
|
||||||
|
RespTimeout: int64(respTimeout),
|
||||||
|
IdleTimeout: int64(idleTimeout),
|
||||||
}
|
}
|
||||||
|
|
||||||
//Add the new upstream to endpoint
|
//Add the new upstream to endpoint
|
||||||
|
@ -57,6 +57,17 @@
|
|||||||
margin-bottom: 0.4em;
|
margin-bottom: 0.4em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.advanceUpstreamOptions{
|
||||||
|
padding: 0.6em;
|
||||||
|
background-color: var(--theme_advance);
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 0.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.advanceUpstreamOptions.ui.accordion .content{
|
||||||
|
padding: 1em !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
@ -121,6 +132,38 @@
|
|||||||
<label>Skip WebSocket Origin Check<br>
|
<label>Skip WebSocket Origin Check<br>
|
||||||
<small>Check this to allow cross-origin websocket requests</small></label>
|
<small>Check this to allow cross-origin websocket requests</small></label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ui advanceUpstreamOptions accordion" style="margin-top:0.6em;">
|
||||||
|
<div class="title">
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
Advanced Options
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<p>Max Concurrent Connections</p>
|
||||||
|
<div class="ui mini fluid input" style="margin-top: -0.6em;">
|
||||||
|
<input type="number" min="0" id="maxConn" value="0">
|
||||||
|
</div>
|
||||||
|
<small>Set to 0 for default value (32 connections)</small>
|
||||||
|
<br><br>
|
||||||
|
<p>Response Timeout</p>
|
||||||
|
<div class="ui mini right labeled fluid input" style="margin-top: -0.6em;">
|
||||||
|
<input type="number" min="0" id="respTimeout" value="0">
|
||||||
|
<div class="ui basic label">
|
||||||
|
Seconds
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<small>Maximum waiting time for server header response, set to 0 for default</small>
|
||||||
|
<br><br>
|
||||||
|
<p>Idle Timeout</p>
|
||||||
|
<div class="ui mini right labeled fluid input" style="margin-top: -0.6em;">
|
||||||
|
<input type="number" min="0" id="idleTimeout" value="0">
|
||||||
|
<div class="ui basic label">
|
||||||
|
Seconds
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<small>Maximum allowed keep-alive time forcefully closes the connection, set to 0 for default</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<br><br>
|
<br><br>
|
||||||
<button class="ui basic button" onclick="addNewUpstream();"><i class="ui green circle add icon"></i> Create</button>
|
<button class="ui basic button" onclick="addNewUpstream();"><i class="ui green circle add icon"></i> Create</button>
|
||||||
</div>
|
</div>
|
||||||
@ -172,6 +215,8 @@
|
|||||||
renderUpstreamEntryToTable(upstream, false);
|
renderUpstreamEntryToTable(upstream, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(".advanceUpstreamOptions.accordion").accordion();
|
||||||
|
|
||||||
let totalUpstreams = data.ActiveOrigins.length + data.InactiveOrigins.length;
|
let totalUpstreams = data.ActiveOrigins.length + data.InactiveOrigins.length;
|
||||||
if (totalUpstreams == 1){
|
if (totalUpstreams == 1){
|
||||||
$(".lowPriorityButton").addClass('disabled');
|
$(".lowPriorityButton").addClass('disabled');
|
||||||
@ -227,6 +272,8 @@
|
|||||||
let url = `${upstream.RequireTLS?"https://":"http://"}${upstream.OriginIpOrDomain}`
|
let url = `${upstream.RequireTLS?"https://":"http://"}${upstream.OriginIpOrDomain}`
|
||||||
let payload = encodeURIComponent(JSON.stringify(upstream));
|
let payload = encodeURIComponent(JSON.stringify(upstream));
|
||||||
let domUID = newUID();
|
let domUID = newUID();
|
||||||
|
|
||||||
|
//Timeout values are stored as ms in the backend
|
||||||
$("#upstreamTable").append(`<div class="ui upstreamEntry ${isActive?"":"inactive"} basic segment" data-domid="${domUID}" data-payload="${payload}" data-priority="${upstream.Priority}">
|
$("#upstreamTable").append(`<div class="ui upstreamEntry ${isActive?"":"inactive"} basic segment" data-domid="${domUID}" data-payload="${payload}" data-priority="${upstream.Priority}">
|
||||||
<h4 class="ui header">
|
<h4 class="ui header">
|
||||||
<div class="ui toggle checkbox" style="display:inline-block;">
|
<div class="ui toggle checkbox" style="display:inline-block;">
|
||||||
@ -262,6 +309,39 @@
|
|||||||
<label>Skip WebSocket Origin Check<br>
|
<label>Skip WebSocket Origin Check<br>
|
||||||
<small>Check this to allow cross-origin websocket requests</small></label>
|
<small>Check this to allow cross-origin websocket requests</small></label>
|
||||||
</div><br>
|
</div><br>
|
||||||
|
<!-- Advance Settings -->
|
||||||
|
<div class="ui advanceUpstreamOptions accordion" style="margin-top:0.6em;">
|
||||||
|
<div class="title">
|
||||||
|
<i class="dropdown icon"></i>
|
||||||
|
Advanced Options
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<p>Max Concurrent Connections</p>
|
||||||
|
<div class="ui mini fluid input" style="margin-top: -0.6em;">
|
||||||
|
<input type="number" min="0" class="maxConn" value="${upstream.MaxConn}">
|
||||||
|
</div>
|
||||||
|
<small>Set to 0 for default value (32 connections)</small>
|
||||||
|
<br>
|
||||||
|
<p style="margin-top: 0.6em;">Response Timeout</p>
|
||||||
|
<div class="ui mini right labeled fluid input" style="margin-top: -0.6em;">
|
||||||
|
<input type="number" min="0" class="respTimeout" value="${upstream.RespTimeout/1000}">
|
||||||
|
<div class="ui basic label">
|
||||||
|
Seconds
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<small>Maximum waiting time before Zoraxy receive server header response, set to 0 for default</small>
|
||||||
|
<br>
|
||||||
|
<p style="margin-top: 0.6em;">Idle Timeout</p>
|
||||||
|
<div class="ui mini right labeled fluid input" style="margin-top: -0.6em;">
|
||||||
|
<input type="number" min="0" class="idleTimeout" value="${upstream.IdleTimeout/1000}">
|
||||||
|
<div class="ui basic label">
|
||||||
|
Seconds
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<small>Maximum allowed keep-alive time before Zoraxy forcefully close the connection, set to 0 for default</small>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="upstreamActions">
|
<div class="upstreamActions">
|
||||||
<!-- Change Priority -->
|
<!-- Change Priority -->
|
||||||
@ -316,12 +396,32 @@
|
|||||||
let skipVerification = $("#skipTlsVerification")[0].checked;
|
let skipVerification = $("#skipTlsVerification")[0].checked;
|
||||||
let skipWebSocketOriginCheck = $("#SkipWebSocketOriginCheck")[0].checked;
|
let skipWebSocketOriginCheck = $("#SkipWebSocketOriginCheck")[0].checked;
|
||||||
let activateLoadbalancer = $("#activateNewUpstreamCheckbox")[0].checked;
|
let activateLoadbalancer = $("#activateNewUpstreamCheckbox")[0].checked;
|
||||||
|
let maxConn = $("#maxConn").val();
|
||||||
|
let respTimeout = $("#respTimeout").val();
|
||||||
|
let idleTimeout = $("#idleTimeout").val();
|
||||||
|
|
||||||
|
if (maxConn == "" || isNaN(maxConn)){
|
||||||
|
maxConn = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (respTimeout == "" || isNaN(respTimeout)){
|
||||||
|
respTimeout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idleTimeout == "" || isNaN(idleTimeout)){
|
||||||
|
idleTimeout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (origin == ""){
|
if (origin == ""){
|
||||||
parent.msgbox("Upstream origin cannot be empty", false);
|
parent.msgbox("Upstream origin cannot be empty", false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Convert seconds to ms
|
||||||
|
respTimeout = parseInt(respTimeout) * 1000;
|
||||||
|
idleTimeout = parseInt(idleTimeout) * 1000;
|
||||||
|
|
||||||
$.cjax({
|
$.cjax({
|
||||||
url: "/api/proxy/upstream/add",
|
url: "/api/proxy/upstream/add",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
@ -332,6 +432,9 @@
|
|||||||
"tlsval": skipVerification,
|
"tlsval": skipVerification,
|
||||||
"bpwsorg":skipWebSocketOriginCheck,
|
"bpwsorg":skipWebSocketOriginCheck,
|
||||||
"active": activateLoadbalancer,
|
"active": activateLoadbalancer,
|
||||||
|
"maxconn": maxConn,
|
||||||
|
"respt": respTimeout,
|
||||||
|
"idlet": idleTimeout,
|
||||||
},
|
},
|
||||||
success: function(data){
|
success: function(data){
|
||||||
if (data.error != undefined){
|
if (data.error != undefined){
|
||||||
@ -340,6 +443,9 @@
|
|||||||
parent.msgbox("New upstream origin added");
|
parent.msgbox("New upstream origin added");
|
||||||
initOriginList();
|
initOriginList();
|
||||||
$("#originURL").val("");
|
$("#originURL").val("");
|
||||||
|
$("#maxConn").val("0");
|
||||||
|
$("#respTimeout").val("0");
|
||||||
|
$("#idleTimeout").val("0");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -356,11 +462,34 @@
|
|||||||
let skipTLSVerification = $(upstream).find(".skipVerificationCheckbox")[0].checked;
|
let skipTLSVerification = $(upstream).find(".skipVerificationCheckbox")[0].checked;
|
||||||
let skipWebSocketOriginCheck = $(upstream).find(".SkipWebSocketOriginCheck")[0].checked;
|
let skipWebSocketOriginCheck = $(upstream).find(".SkipWebSocketOriginCheck")[0].checked;
|
||||||
|
|
||||||
|
//Advance options
|
||||||
|
let maxConn = $(upstream).find(".maxConn").val();
|
||||||
|
let respTimeout = $(upstream).find(".respTimeout").val();
|
||||||
|
let idleTimeout = $(upstream).find(".idleTimeout").val();
|
||||||
|
|
||||||
|
if (maxConn == "" || isNaN(maxConn)){
|
||||||
|
maxConn = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (respTimeout == "" || isNaN(respTimeout)){
|
||||||
|
respTimeout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idleTimeout == "" || isNaN(idleTimeout)){
|
||||||
|
idleTimeout = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
respTimeout = parseInt(respTimeout) * 1000;
|
||||||
|
idleTimeout = parseInt(idleTimeout) * 1000;
|
||||||
|
|
||||||
//Update the original setting with new one just applied
|
//Update the original setting with new one just applied
|
||||||
originalSettings.OriginIpOrDomain = $(upstream).find(".newOrigin").val();
|
originalSettings.OriginIpOrDomain = $(upstream).find(".newOrigin").val();
|
||||||
originalSettings.RequireTLS = requireTLS;
|
originalSettings.RequireTLS = requireTLS;
|
||||||
originalSettings.SkipCertValidations = skipTLSVerification;
|
originalSettings.SkipCertValidations = skipTLSVerification;
|
||||||
originalSettings.SkipWebSocketOriginCheck = skipWebSocketOriginCheck;
|
originalSettings.SkipWebSocketOriginCheck = skipWebSocketOriginCheck;
|
||||||
|
originalSettings.MaxConn = parseInt(maxConn);
|
||||||
|
originalSettings.RespTimeout = respTimeout;
|
||||||
|
originalSettings.IdleTimeout = idleTimeout;
|
||||||
|
|
||||||
//console.log(originalSettings);
|
//console.log(originalSettings);
|
||||||
return originalSettings;
|
return originalSettings;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user