Skip to content

Commit

Permalink
Merge pull request #522 from tobychui/v3.1.7
Browse files Browse the repository at this point in the history
- Merged and added new tagging system for HTTP Proxy rules
- Added inline editing for redirection rules
- Added uptime monitor status dot detail info (now clickable)
- Added close connection support to port 80 listener
- Optimized port collision check on startup
- Optimized dark theme color scheme (Free consultation by [3S Design studio](https://www.3sdesign.io/))
- Fixed capital letter rule unable to delete bug
  • Loading branch information
tobychui authored Feb 8, 2025
2 parents d1e5581 + 693dba0 commit a728543
Show file tree
Hide file tree
Showing 25 changed files with 34,565 additions and 28,135 deletions.
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,9 @@ src/tmp/localhost.pem
src/www/html/index.html
src/sys.uuid
src/zoraxy
src/log/
src/log/


# dev-tags
/Dockerfile
/Entrypoint.sh
21 changes: 18 additions & 3 deletions docker/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
#!/usr/bin/env bash

trap cleanup TERM INT

cleanup() {
echo "Shutting down..."
kill -TERM "$(pidof zoraxy)" &> /dev/null && echo "Zoraxy stopped."
kill -TERM "$(pidof zerotier-one)" &> /dev/null && echo "ZeroTier-One stopped."
exit 0
}

update-ca-certificates
echo "CA certificates updated."

Expand All @@ -11,12 +20,13 @@ if [ "$ZEROTIER" = "true" ]; then
mkdir -p /opt/zoraxy/config/zerotier/
fi
ln -s /opt/zoraxy/config/zerotier/ /var/lib/zerotier-one
zerotier-one -d
zerotier-one -d &
zerotierpid=$!
echo "ZeroTier daemon started."
fi

echo "Starting Zoraxy..."
exec zoraxy \
zoraxy \
-autorenew="$AUTORENEW" \
-cfgupgrade="$CFGUPGRADE" \
-db="$DB" \
Expand All @@ -33,5 +43,10 @@ exec zoraxy \
-webfm="$WEBFM" \
-webroot="$WEBROOT" \
-ztauth="$ZTAUTH" \
-ztport="$ZTPORT"
-ztport="$ZTPORT" \
&

zoraxypid=$!
wait $zoraxypid
wait $zerotierpid

1 change: 1 addition & 0 deletions src/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ func RegisterRedirectionAPIs(authRouter *auth.RouterDef) {
authRouter.HandleFunc("/api/redirect/list", handleListRedirectionRules)
authRouter.HandleFunc("/api/redirect/add", handleAddRedirectionRule)
authRouter.HandleFunc("/api/redirect/delete", handleDeleteRedirectionRule)
authRouter.HandleFunc("/api/redirect/edit", handleEditRedirectionRule)
authRouter.HandleFunc("/api/redirect/regex", handleToggleRedirectRegexpSupport)
}

Expand Down
9 changes: 7 additions & 2 deletions src/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ func LoadReverseProxyConfig(configFilepath string) error {
return err
}

//Make sure the tags are not nil
if thisConfigEndpoint.Tags == nil {
thisConfigEndpoint.Tags = []string{}
}

//Matching domain not set. Assume root
if thisConfigEndpoint.RootOrMatchingDomain == "" {
thisConfigEndpoint.RootOrMatchingDomain = "/"
Expand Down Expand Up @@ -175,8 +180,8 @@ func ExportConfigAsZip(w http.ResponseWriter, r *http.Request) {

// Set the Content-Type header to indicate it's a zip file
w.Header().Set("Content-Type", "application/zip")
// Set the Content-Disposition header to specify the file name
w.Header().Set("Content-Disposition", "attachment; filename=\"config.zip\"")
// Set the Content-Disposition header to specify the file name, add timestamp to the filename
w.Header().Set("Content-Disposition", "attachment; filename=\"zoraxy-config-"+time.Now().Format("2006-01-02-15-04-05")+".zip\"")

// Create a zip writer
zipWriter := zip.NewWriter(w)
Expand Down
6 changes: 5 additions & 1 deletion src/def.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import (
const (
/* Build Constants */
SYSTEM_NAME = "Zoraxy"
SYSTEM_VERSION = "3.1.6"
SYSTEM_VERSION = "3.1.7"
DEVELOPMENT_BUILD = false /* Development: Set to false to use embedded web fs */

/* System Constants */
Expand Down Expand Up @@ -87,6 +87,10 @@ var (
allowWebFileManager = flag.Bool("webfm", true, "Enable web file manager for static web server root folder")
enableAutoUpdate = flag.Bool("cfgupgrade", true, "Enable auto config upgrade if breaking change is detected")

/* Default Configuration Flags */
defaultInboundPort = flag.Int("default_inbound_port", 443, "Default web server listening port")
defaultEnableInboundTraffic = flag.Bool("default_inbound_enabled", true, "If web server is enabled by default")

/* Path Configuration Flags */
//path_database = flag.String("dbpath", "./sys.db", "Database path")
//path_conf = flag.String("conf", "./conf", "Configuration folder path")
Expand Down
27 changes: 16 additions & 11 deletions src/mod/dynamicproxy/Server.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,25 +209,18 @@ func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request)
http.Redirect(w, r, redirectTarget, http.StatusTemporaryRedirect)
case DefaultSite_NotFoundPage:
//Serve the not found page, use template if exists
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusNotFound)
template, err := os.ReadFile(filepath.Join(h.Parent.Option.WebDirectory, "templates/notfound.html"))
if err != nil {
w.Write(page_hosterror)
} else {
w.Write(template)
}
h.serve404PageWithTemplate(w, r)
case DefaultSite_NoResponse:
//No response. Just close the connection
h.Parent.logRequest(r, false, 444, "root-noresponse", domainOnly)
h.Parent.logRequest(r, false, 444, "root-no_resp", domainOnly)
hijacker, ok := w.(http.Hijacker)
if !ok {
w.Header().Set("Connection", "close")
w.WriteHeader(http.StatusNoContent)
return
}
conn, _, err := hijacker.Hijack()
if err != nil {
w.Header().Set("Connection", "close")
w.WriteHeader(http.StatusNoContent)
return
}
conn.Close()
Expand All @@ -241,3 +234,15 @@ func (h *ProxyHandler) handleRootRouting(w http.ResponseWriter, r *http.Request)
http.Error(w, "544 - No Route Defined", 544)
}
}

// Serve 404 page with template if exists
func (h *ProxyHandler) serve404PageWithTemplate(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html; charset=utf-8")
w.WriteHeader(http.StatusNotFound)
template, err := os.ReadFile(filepath.Join(h.Parent.Option.WebDirectory, "templates/notfound.html"))
if err != nil {
w.Write(page_hosterror)
} else {
w.Write(template)
}
}
1 change: 1 addition & 0 deletions src/mod/dynamicproxy/domainsniff/proxmox.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ func IsProxmox(r *http.Request) bool {
return true
}
}

return false
}
14 changes: 13 additions & 1 deletion src/mod/dynamicproxy/dpcore/dpcore.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dpcore

import (
"context"
"crypto/tls"
"errors"
"io"
"log"
Expand All @@ -11,6 +12,7 @@ import (
"strings"
"time"

"golang.org/x/net/http2"
"imuslab.com/zoraxy/mod/dynamicproxy/domainsniff"
"imuslab.com/zoraxy/mod/dynamicproxy/permissionpolicy"
)
Expand Down Expand Up @@ -84,6 +86,7 @@ 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
}

func NewDynamicProxyCore(target *url.URL, prepender string, dpcOptions *DpcoreOptions) *ReverseProxy {
Expand All @@ -100,8 +103,17 @@ func NewDynamicProxyCore(target *url.URL, prepender string, dpcOptions *DpcoreOp

}

//Hack the default transporter to handle more connections
thisTransporter := http.DefaultTransport
if dpcOptions.UseH2CRoundTripper {
thisTransporter = &http2.Transport{
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
return net.Dial(network, addr)
},
AllowHTTP: true,
}
}

//Hack the default transporter to handle more connections
optimalConcurrentConnection := 32
thisTransporter.(*http.Transport).MaxIdleConns = optimalConcurrentConnection * 2
thisTransporter.(*http.Transport).MaxIdleConnsPerHost = optimalConcurrentConnection
Expand Down
21 changes: 19 additions & 2 deletions src/mod/dynamicproxy/dynamicproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,24 @@ func (router *Router) StartProxyService() error {
w.Write([]byte("400 - Bad Request"))
} else {
//No defined sub-domain
http.NotFound(w, r)
if router.Root.DefaultSiteOption == DefaultSite_NoResponse {
//No response. Just close the connection
hijacker, ok := w.(http.Hijacker)
if !ok {
w.Header().Set("Connection", "close")
return
}
conn, _, err := hijacker.Hijack()
if err != nil {
w.Header().Set("Connection", "close")
return
}
conn.Close()
} else {
//Default behavior
http.NotFound(w, r)
}

}

}
Expand Down Expand Up @@ -337,7 +354,7 @@ func (router *Router) LoadProxy(matchingDomain string) (*ProxyEndpoint, error) {
return true
}

if key == matchingDomain {
if key == strings.ToLower(matchingDomain) {
targetProxyEndpoint = v
}
return true
Expand Down
3 changes: 2 additions & 1 deletion src/mod/dynamicproxy/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,8 @@ func (ep *ProxyEndpoint) Clone() *ProxyEndpoint {

// Remove this proxy endpoint from running proxy endpoint list
func (ep *ProxyEndpoint) Remove() error {
ep.parent.ProxyEndpoints.Delete(ep.RootOrMatchingDomain)
lookupHostname := strings.ToLower(ep.RootOrMatchingDomain)
ep.parent.ProxyEndpoints.Delete(lookupHostname)
return nil
}

Expand Down
38 changes: 36 additions & 2 deletions src/mod/dynamicproxy/redirection/redirection.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package redirection

import (
"encoding/json"
"fmt"
"log"
"os"
"path"
Expand Down Expand Up @@ -111,14 +110,49 @@ func (t *RuleTable) AddRedirectRule(redirectURL string, destURL string, forwardP
return nil
}

// Edit an existing redirection rule, the oldRedirectURL is used to find the rule to be edited
func (t *RuleTable) EditRedirectRule(oldRedirectURL string, newRedirectURL string, destURL string, forwardPathname bool, statusCode int) error {
newRule := &RedirectRules{
RedirectURL: newRedirectURL,
TargetURL: destURL,
ForwardChildpath: forwardPathname,
StatusCode: statusCode,
}

//Remove the old rule
t.DeleteRedirectRule(oldRedirectURL)

// Convert the redirectURL to a valid filename by replacing "/" with "-" and "." with "_"
filename := utils.ReplaceSpecialCharacters(newRedirectURL) + ".json"
filepath := path.Join(t.configPath, filename)

// Create a new file for writing the JSON data
file, err := os.Create(filepath)
if err != nil {
t.log("Error creating file "+filepath, err)
return err
}
defer file.Close()

err = json.NewEncoder(file).Encode(newRule)
if err != nil {
t.log("Error encoding JSON to file "+filepath, err)
return err
}

// Update the runtime map
t.rules.Store(newRedirectURL, newRule)

return nil
}

func (t *RuleTable) DeleteRedirectRule(redirectURL string) error {
// Convert the redirectURL to a valid filename by replacing "/" with "-" and "." with "_"
filename := utils.ReplaceSpecialCharacters(redirectURL) + ".json"

// Create the full file path by joining the t.configPath with the filename
filepath := path.Join(t.configPath, filename)

fmt.Println(redirectURL, filename, filepath)
// Check if the file exists
if _, err := os.Stat(filepath); os.IsNotExist(err) {
return nil // File doesn't exist, nothing to delete
Expand Down
1 change: 1 addition & 0 deletions src/mod/dynamicproxy/typedef.go
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ type ProxyEndpoint struct {

//Internal Logic Elements
parent *Router `json:"-"`
Tags []string // Tags for the proxy endpoint
}

/*
Expand Down
Loading

0 comments on commit a728543

Please sign in to comment.