adhoc-server/main.go_with_go_embed
2024-01-28 07:22:52 +03:30

130 lines
3.6 KiB
Plaintext

package main
import (
"crypto/tls"
"embed"
"flag"
"fmt"
"log"
"net/http"
"path/filepath"
"time"
)
//go:embed certs/cert.pem certs/key.pem
var embeddedFiles embed.FS
// Constants for version and help text
const version = "1.0.0"
const helpText = `Usage: https_server [options] [path]
Options:
-b, --bind The address to bind the HTTPS server (default "localhost:4443").
-v, --version Display the version of the server.
-h, --help Display this help message.
Path:
The path of the directory to serve. Defaults to the current directory.`
// CustomResponseWriter is a wrapper around http.ResponseWriter
// that captures the status code of the HTTP response.
type CustomResponseWriter struct {
http.ResponseWriter
statusCode int
}
// NewCustomResponseWriter creates a new CustomResponseWriter.
func NewCustomResponseWriter(w http.ResponseWriter) *CustomResponseWriter {
// Default the status code to 200, as WriteHeader might not be called explicitly
return &CustomResponseWriter{w, http.StatusOK}
}
// WriteHeader captures the status code and delegates to the original writer.
func (w *CustomResponseWriter) WriteHeader(statusCode int) {
w.statusCode = statusCode
w.ResponseWriter.WriteHeader(statusCode)
}
// Custom logging handler
func loggingHandler(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Log the incoming request
start := time.Now()
log.Printf("Started %s %s from %s", r.Method, r.URL.Path, r.RemoteAddr)
customWriter := NewCustomResponseWriter(w)
// Call the next handler
next.ServeHTTP(customWriter, r)
// Log the request and response details
log.Printf("%s %s from %s - %d in %v", r.Method, r.URL.Path, r.RemoteAddr, customWriter.statusCode, time.Since(start))
})
}
func main() {
versionFlag := flag.Bool("version", false, "Display the version of the server")
versionFlagShort := flag.Bool("v", false, "Display the version (short)")
helpFlag := flag.Bool("help", false, "Display help message")
helpFlagShort := flag.Bool("h", false, "Display help message (short)")
bindAddr := flag.String("bind", "localhost:4443", "The address to bind the HTTPS server")
bindAddrShort := flag.String("b", "localhost:4443", "The address to bind the HTTPS server (short)")
flag.Parse()
if *bindAddrShort != "localhost:4443" {
bindAddr = bindAddrShort
}
// Handle version and help flags
if *versionFlag || *versionFlagShort {
fmt.Println("Version:", version)
return
}
if *helpFlag || *helpFlagShort {
fmt.Println(helpText)
return
}
// Determine the directory to serve
dir := "."
if flag.NArg() > 0 {
dir = flag.Arg(0)
}
dir, err := filepath.Abs(dir)
if err != nil {
log.Fatalf("Error determining absolute path: %v", err)
}
// Read the embedded certificate and key from the certs directory
certData, err := embeddedFiles.ReadFile("certs/cert.pem")
if err != nil {
log.Fatalf("Failed to read embedded certificate: %v", err)
}
keyData, err := embeddedFiles.ReadFile("certs/key.pem")
if err != nil {
log.Fatalf("Failed to read embedded key: %v", err)
}
// Load the certificate and key
cert, err := tls.X509KeyPair(certData, keyData)
if err != nil {
log.Fatalf("Failed to parse certificate and key: %v", err)
}
// Create a file server handler
fileServer := http.FileServer(http.Dir(dir))
// Wrap the file server with the logging handler
loggedFS := loggingHandler(fileServer)
server := &http.Server{
Addr: *bindAddr,
TLSConfig: &tls.Config{
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
},
Handler: loggedFS,
}
log.Printf("Serving %s at https://%s\n", dir, *bindAddr)
log.Fatal(server.ListenAndServeTLS("", ""))
}