mirror of
				https://github.com/tobychui/zoraxy.git
				synced 2025-11-04 07:54:12 +01:00 
			
		
		
		
	Static web server
- Fixed arm64 build bug in Make file - Added wip static web server
This commit is contained in:
		@@ -19,7 +19,8 @@ clean:
 | 
			
		||||
 | 
			
		||||
$(PLATFORMS):
 | 
			
		||||
	@echo "Building $(os)/$(arch)"
 | 
			
		||||
	GOROOT_FINAL=Git/ GOOS=$(os) GOARCH=$(arch) GOARM=6 go build -o './dist/zoraxy_$(os)_$(arch)'  -ldflags "-s -w" -trimpath
 | 
			
		||||
	GOROOT_FINAL=Git/ GOOS=$(os) GOARCH=$(arch) $(if $(filter linux/arm,$(os)/$(arch)),GOARM=6,) go build -o './dist/zoraxy_$(os)_$(arch)'  -ldflags "-s -w" -trimpath
 | 
			
		||||
#	GOROOT_FINAL=Git/ GOOS=$(os) GOARCH=$(arch) GOARM=6 go build -o './dist/zoraxy_$(os)_$(arch)'  -ldflags "-s -w" -trimpath
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
fixwindows:
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@ import (
 | 
			
		||||
	"imuslab.com/zoraxy/mod/tlscert"
 | 
			
		||||
	"imuslab.com/zoraxy/mod/uptime"
 | 
			
		||||
	"imuslab.com/zoraxy/mod/utils"
 | 
			
		||||
	"imuslab.com/zoraxy/mod/webserv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// General flags
 | 
			
		||||
@@ -41,6 +42,8 @@ var ztAuthToken = flag.String("ztauth", "", "ZeroTier authtoken for the local no
 | 
			
		||||
var ztAPIPort = flag.Int("ztport", 9993, "ZeroTier controller API port")
 | 
			
		||||
var acmeAutoRenewInterval = flag.Int("autorenew", 86400, "ACME auto TLS/SSL certificate renew check interval (seconds)")
 | 
			
		||||
var enableHighSpeedGeoIPLookup = flag.Bool("fastgeoip", false, "Enable high speed geoip lookup, require 1GB extra memory (Not recommend for low end devices)")
 | 
			
		||||
var staticWebServerRoot = flag.String("webroot", "./www", "Static web server root folder. Only allow chnage in start paramters")
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	name        = "Zoraxy"
 | 
			
		||||
	version     = "2.6.7"
 | 
			
		||||
@@ -73,6 +76,7 @@ var (
 | 
			
		||||
	tcpProxyManager    *tcpprox.Manager        //TCP Proxy Manager
 | 
			
		||||
	acmeHandler        *acme.ACMEHandler       //Handler for ACME Certificate renew
 | 
			
		||||
	acmeAutoRenewer    *acme.AutoRenewer       //Handler for ACME auto renew ticking
 | 
			
		||||
	staticWebServer    *webserv.WebServer      //Static web server for hosting simple stuffs
 | 
			
		||||
 | 
			
		||||
	//Helper modules
 | 
			
		||||
	EmailSender    *email.Sender        //Email sender that handle email sending
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										9
									
								
								src/mod/webserv/handler.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/mod/webserv/handler.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
package webserv
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
	Handler.go
 | 
			
		||||
 | 
			
		||||
	Handler for web server options change
 | 
			
		||||
	web server is directly listening to the TCP port
 | 
			
		||||
	handlers in this script are for setting change only
 | 
			
		||||
*/
 | 
			
		||||
							
								
								
									
										41
									
								
								src/mod/webserv/middleware.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/mod/webserv/middleware.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,41 @@
 | 
			
		||||
package webserv
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"imuslab.com/zoraxy/mod/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Convert a request path (e.g. /index.html) into physical path on disk
 | 
			
		||||
func (ws *WebServer) resolveFileDiskPath(requestPath string) string {
 | 
			
		||||
	fileDiskpath := filepath.Join(ws.option.WebRoot, "html", requestPath)
 | 
			
		||||
 | 
			
		||||
	//Force convert it to slash even if the host OS is on Windows
 | 
			
		||||
	fileDiskpath = filepath.Clean(fileDiskpath)
 | 
			
		||||
	fileDiskpath = strings.ReplaceAll(fileDiskpath, "\\", "/")
 | 
			
		||||
	return fileDiskpath
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// File server middleware to handle directory listing (and future expansion)
 | 
			
		||||
func (ws *WebServer) fsMiddleware(h http.Handler) http.Handler {
 | 
			
		||||
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
		if ws.option.EnableDirectoryListing {
 | 
			
		||||
			if strings.HasSuffix(r.URL.Path, "/") {
 | 
			
		||||
				//This is a folder. Let check if index exists
 | 
			
		||||
				if utils.FileExists(filepath.Join(ws.resolveFileDiskPath(r.URL.Path), "index.html")) {
 | 
			
		||||
 | 
			
		||||
				} else if utils.FileExists(filepath.Join(ws.resolveFileDiskPath(r.URL.Path), "index.htm")) {
 | 
			
		||||
 | 
			
		||||
				} else {
 | 
			
		||||
					http.NotFound(w, r)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		h.ServeHTTP(w, r)
 | 
			
		||||
	})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								src/mod/webserv/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								src/mod/webserv/utils.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
package webserv
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// IsPortInUse checks if a port is in use.
 | 
			
		||||
func IsPortInUse(port string) bool {
 | 
			
		||||
	listener, err := net.Listen("tcp", "localhost:"+port)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		// If there was an error, the port is in use.
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
	defer listener.Close()
 | 
			
		||||
 | 
			
		||||
	// No error means the port is available.
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										137
									
								
								src/mod/webserv/webserv.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								src/mod/webserv/webserv.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
			
		||||
package webserv
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"log"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"imuslab.com/zoraxy/mod/utils"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
	Static Web Server package
 | 
			
		||||
 | 
			
		||||
	This module host a static web server
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
type WebServerOptions struct {
 | 
			
		||||
	Port                   string //Port for listening
 | 
			
		||||
	EnableDirectoryListing bool   //Enable listing of directory
 | 
			
		||||
	WebRoot                string //Folder for stroing the static web folders
 | 
			
		||||
}
 | 
			
		||||
type WebServer struct {
 | 
			
		||||
	mux       *http.ServeMux
 | 
			
		||||
	server    *http.Server
 | 
			
		||||
	option    *WebServerOptions
 | 
			
		||||
	isRunning bool
 | 
			
		||||
	mu        sync.Mutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewWebServer creates a new WebServer instance.
 | 
			
		||||
func NewWebServer(options *WebServerOptions) *WebServer {
 | 
			
		||||
	if !utils.FileExists(options.WebRoot) {
 | 
			
		||||
		//Web root folder not exists. Create one
 | 
			
		||||
		os.MkdirAll(filepath.Join(options.WebRoot, "html"), 0775)
 | 
			
		||||
		os.MkdirAll(filepath.Join(options.WebRoot, "templates"), 0775)
 | 
			
		||||
	}
 | 
			
		||||
	return &WebServer{
 | 
			
		||||
		mux:       http.NewServeMux(),
 | 
			
		||||
		option:    options,
 | 
			
		||||
		isRunning: false,
 | 
			
		||||
		mu:        sync.Mutex{},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ChangePort changes the server's port.
 | 
			
		||||
func (ws *WebServer) ChangePort(port string) error {
 | 
			
		||||
	ws.mu.Lock()
 | 
			
		||||
	defer ws.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	if ws.isRunning {
 | 
			
		||||
		if err := ws.Stop(); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ws.option.Port = port
 | 
			
		||||
	ws.server.Addr = ":" + port
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Start starts the web server.
 | 
			
		||||
func (ws *WebServer) Start() error {
 | 
			
		||||
	ws.mu.Lock()
 | 
			
		||||
	defer ws.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	//Check if server already running
 | 
			
		||||
	if ws.isRunning {
 | 
			
		||||
		return fmt.Errorf("web server is already running")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//Check if the port is usable
 | 
			
		||||
	if IsPortInUse(ws.option.Port) {
 | 
			
		||||
		return errors.New("Port already in use or access denied by host OS")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//Dispose the old mux and create a new one
 | 
			
		||||
	ws.mux = http.NewServeMux()
 | 
			
		||||
 | 
			
		||||
	//Create a static web server
 | 
			
		||||
	fs := http.FileServer(http.Dir(filepath.Join(ws.option.WebRoot, "html")))
 | 
			
		||||
	ws.mux.Handle("/", ws.fsMiddleware(fs))
 | 
			
		||||
 | 
			
		||||
	ws.server = &http.Server{
 | 
			
		||||
		Addr:    ":" + ws.option.Port,
 | 
			
		||||
		Handler: ws.mux,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		if err := ws.server.ListenAndServe(); err != nil {
 | 
			
		||||
			if err != http.ErrServerClosed {
 | 
			
		||||
				fmt.Printf("Web server error: %v\n", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	log.Println("Static Web Server started. Listeing on :" + ws.option.Port)
 | 
			
		||||
	ws.isRunning = true
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stop stops the web server.
 | 
			
		||||
func (ws *WebServer) Stop() error {
 | 
			
		||||
	ws.mu.Lock()
 | 
			
		||||
	defer ws.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	if !ws.isRunning {
 | 
			
		||||
		return fmt.Errorf("web server is not running")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := ws.server.Close(); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ws.isRunning = false
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateDirectoryListing enables or disables directory listing.
 | 
			
		||||
func (ws *WebServer) UpdateDirectoryListing(enable bool) {
 | 
			
		||||
	ws.mu.Lock()
 | 
			
		||||
	defer ws.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	ws.option.EnableDirectoryListing = enable
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close stops the web server without returning an error.
 | 
			
		||||
func (ws *WebServer) Close() {
 | 
			
		||||
	ws.Stop()
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								src/start.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								src/start.go
									
									
									
									
									
								
							@@ -22,6 +22,7 @@ import (
 | 
			
		||||
	"imuslab.com/zoraxy/mod/statistic/analytic"
 | 
			
		||||
	"imuslab.com/zoraxy/mod/tcpprox"
 | 
			
		||||
	"imuslab.com/zoraxy/mod/tlscert"
 | 
			
		||||
	"imuslab.com/zoraxy/mod/webserv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -208,6 +209,21 @@ func startupSequence() {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
		Static Web Server
 | 
			
		||||
 | 
			
		||||
		Start the static web server
 | 
			
		||||
	*/
 | 
			
		||||
 | 
			
		||||
	staticWebServer = webserv.NewWebServer(&webserv.WebServerOptions{
 | 
			
		||||
		Port:                   "8081",
 | 
			
		||||
		WebRoot:                *staticWebServerRoot,
 | 
			
		||||
		EnableDirectoryListing: true,
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	//TODO: Connect UI element to static web server
 | 
			
		||||
	staticWebServer.Start()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// This sequence start after everything is initialized
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user