Static web server

- Fixed arm64 build bug in Make file
- Added wip static web server
This commit is contained in:
Toby Chui 2023-09-12 16:41:52 +08:00
parent c65f780613
commit a692ec818d
7 changed files with 227 additions and 1 deletions

View File

@ -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:

View File

@ -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

View 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
*/

View 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
View 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
View 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()
}

View File

@ -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