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:
Toby Chui 2025-02-16 16:58:25 +08:00
parent 36c1f149e6
commit caf4ab331b
7 changed files with 14024 additions and 11562 deletions

View File

@ -83,9 +83,12 @@ type requestCanceler interface {
}
type DpcoreOptions struct {
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)
UseH2CRoundTripper bool //Use H2C RoundTripper for HTTP/2.0 connection
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)
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 {
@ -106,18 +109,30 @@ func NewDynamicProxyCore(target *url.URL, prepender string, dpcOptions *DpcoreOp
//Hack the default transporter to handle more connections
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).MaxIdleConnsPerHost = optimalConcurrentConnection
thisTransporter.(*http.Transport).IdleConnTimeout = 30 * time.Second
thisTransporter.(*http.Transport).MaxConnsPerHost = optimalConcurrentConnection * 2
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 {
//Ignore TLS certificate validation error
thisTransporter.(*http.Transport).TLSClientConfig.InsecureSkipVerify = true
}
//TODO: Add user adjustable timeout option here
if dpcOptions.UseH2CRoundTripper {
//Use H2C RoundTripper for HTTP/2.0 connection
thisTransporter = modh2c.NewH2CRoundTripper()

View File

@ -43,8 +43,12 @@ type Upstream struct {
SkipWebSocketOriginCheck bool //Skip origin check on websocket upgrade connections
//Load balancing configs
Weight int //Random weight for round robin, 0 for fallback only
MaxConn int //TODO: Maxmium connection to this server, 0 for unlimited
Weight int //Random weight for round robin, 0 for fallback only
//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
proxy *dpcore.ReverseProxy

View File

@ -39,8 +39,11 @@ func (u *Upstream) StartProxy() error {
}
proxy := dpcore.NewDynamicProxyCore(path, "", &dpcore.DpcoreOptions{
IgnoreTLSVerification: u.SkipCertValidations,
FlushInterval: 100 * time.Millisecond,
IgnoreTLSVerification: u.SkipCertValidations,
FlushInterval: 100 * time.Millisecond,
ResponseHeaderTimeout: u.RespTimeout,
IdleConnectionTimeout: u.IdleTimeout,
MaxConcurrentConnection: u.MaxConn,
})
u.proxy = proxy

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -79,6 +79,25 @@ func ReverseProxyUpstreamAdd(w http.ResponseWriter, r *http.Request) {
utils.SendErrorResponse(w, "upstream origin not set")
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")
skipTlsValidation, _ := utils.PostBool(r, "tlsval")
bpwsorg, _ := utils.PostBool(r, "bpwsorg")
@ -91,7 +110,9 @@ func ReverseProxyUpstreamAdd(w http.ResponseWriter, r *http.Request) {
SkipCertValidations: skipTlsValidation,
SkipWebSocketOriginCheck: bpwsorg,
Weight: 1,
MaxConn: 0,
MaxConn: maxConn,
RespTimeout: int64(respTimeout),
IdleTimeout: int64(idleTimeout),
}
//Add the new upstream to endpoint

View File

@ -57,6 +57,17 @@
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>
</head>
<body>
@ -121,6 +132,38 @@
<label>Skip WebSocket Origin Check<br>
<small>Check this to allow cross-origin websocket requests</small></label>
</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>
<button class="ui basic button" onclick="addNewUpstream();"><i class="ui green circle add icon"></i> Create</button>
</div>
@ -172,6 +215,8 @@
renderUpstreamEntryToTable(upstream, false);
});
$(".advanceUpstreamOptions.accordion").accordion();
let totalUpstreams = data.ActiveOrigins.length + data.InactiveOrigins.length;
if (totalUpstreams == 1){
$(".lowPriorityButton").addClass('disabled');
@ -227,6 +272,8 @@
let url = `${upstream.RequireTLS?"https://":"http://"}${upstream.OriginIpOrDomain}`
let payload = encodeURIComponent(JSON.stringify(upstream));
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}">
<h4 class="ui header">
<div class="ui toggle checkbox" style="display:inline-block;">
@ -262,6 +309,39 @@
<label>Skip WebSocket Origin Check<br>
<small>Check this to allow cross-origin websocket requests</small></label>
</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 class="upstreamActions">
<!-- Change Priority -->
@ -316,12 +396,32 @@
let skipVerification = $("#skipTlsVerification")[0].checked;
let skipWebSocketOriginCheck = $("#SkipWebSocketOriginCheck")[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 == ""){
parent.msgbox("Upstream origin cannot be empty", false);
return;
}
//Convert seconds to ms
respTimeout = parseInt(respTimeout) * 1000;
idleTimeout = parseInt(idleTimeout) * 1000;
$.cjax({
url: "/api/proxy/upstream/add",
method: "POST",
@ -332,6 +432,9 @@
"tlsval": skipVerification,
"bpwsorg":skipWebSocketOriginCheck,
"active": activateLoadbalancer,
"maxconn": maxConn,
"respt": respTimeout,
"idlet": idleTimeout,
},
success: function(data){
if (data.error != undefined){
@ -340,6 +443,9 @@
parent.msgbox("New upstream origin added");
initOriginList();
$("#originURL").val("");
$("#maxConn").val("0");
$("#respTimeout").val("0");
$("#idleTimeout").val("0");
}
}
})
@ -356,11 +462,34 @@
let skipTLSVerification = $(upstream).find(".skipVerificationCheckbox")[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
originalSettings.OriginIpOrDomain = $(upstream).find(".newOrigin").val();
originalSettings.RequireTLS = requireTLS;
originalSettings.SkipCertValidations = skipTLSVerification;
originalSettings.SkipWebSocketOriginCheck = skipWebSocketOriginCheck;
originalSettings.MaxConn = parseInt(maxConn);
originalSettings.RespTimeout = respTimeout;
originalSettings.IdleTimeout = idleTimeout;
//console.log(originalSettings);
return originalSettings;