130 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			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("", ""))
 | 
						|
}
 |