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

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