Skip to content

Instantly share code, notes, and snippets.

@abdusco
Last active December 24, 2024 19:35
Show Gist options
  • Save abdusco/c2d46ad01946ddeaf7c5246097d9f8de to your computer and use it in GitHub Desktop.
Save abdusco/c2d46ad01946ddeaf7c5246097d9f8de to your computer and use it in GitHub Desktop.
Open up HTML from stdin in a browser tab
package main
import (
"context"
"fmt"
"io"
"log"
"net"
"net/http"
"os"
"os/exec"
"os/signal"
"runtime"
"strings"
"syscall"
"time"
)
// language=html
const injectScript = `
<script>
const sendShutdown = () => navigator.sendBeacon('/shutdown');
window.addEventListener('beforeunload', sendShutdown);
window.addEventListener('unload', sendShutdown);
</script>
`
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
func newHandler(html string, onShutdown func()) *http.ServeMux {
handler := http.NewServeMux()
handler.HandleFunc("POST /shutdown", func(w http.ResponseWriter, r *http.Request) {
onShutdown()
})
handler.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
w.Write([]byte(html))
})
return handler
}
func run() error {
html, err := io.ReadAll(os.Stdin)
if err != nil {
return fmt.Errorf("failed to read HTML from stdin: %w", err)
}
htmlStr := string(html)
if strings.Contains(htmlStr, "</body>") {
htmlStr = strings.ReplaceAll(string(html), "</body>", injectScript+"</body>")
} else {
htmlStr += injectScript
}
listener, err := net.Listen("tcp", ":0")
if err != nil {
return fmt.Errorf("failed to create listener: %w", err)
}
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer cancel()
port := listener.Addr().(*net.TCPAddr).Port
server := &http.Server{Addr: listener.Addr().String()}
server.Handler = newHandler(htmlStr, func() {
cancel()
})
servedURL := fmt.Sprintf("http://localhost:%d", port)
go func() {
log.Printf("serving at %s", servedURL)
if err := server.Serve(listener); err != nil && err != http.ErrServerClosed {
log.Fatalf("failed to serve: %v", err)
}
}()
go func() {
// wait for server to start
time.Sleep(250 * time.Millisecond)
if err := openBrowser(servedURL); err != nil {
log.Fatalf("failed to open browser: %v", err)
}
}()
<-ctx.Done()
log.Printf("shutting down server")
if err := server.Shutdown(ctx); err != nil {
return fmt.Errorf("failed to shutdown server: %w", err)
}
return nil
}
func openBrowser(url string) error {
var cmd *exec.Cmd
switch runtime.GOOS {
case "darwin":
cmd = exec.Command("open", url)
case "linux":
cmd = exec.Command("xdg-open", url)
case "windows":
cmd = exec.Command("cmd", "/c", "start", url)
default:
return fmt.Errorf("invalid URL: %s", url)
}
return cmd.Run()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment