Skip to content

Commit b41eb6b

Browse files
feat: wire main entrypoint with log-mode default
1 parent 39ee1dc commit b41eb6b

2 files changed

Lines changed: 126 additions & 1 deletion

File tree

main.go

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,118 @@
11
package main
22

3-
func main() {}
3+
import (
4+
"context"
5+
"crypto/tls"
6+
"errors"
7+
"flag"
8+
"fmt"
9+
"net/http"
10+
"os"
11+
"os/signal"
12+
"strconv"
13+
"syscall"
14+
"time"
15+
)
16+
17+
const eventBuffer = 256
18+
19+
func main() {
20+
listen := flag.Int("listen", 3443, "HTTPS listen port")
21+
host := flag.String("host", "", "additional SAN for cert (e.g. api.local)")
22+
lan := flag.Bool("lan", false, "bind on 0.0.0.0 for LAN access (default 127.0.0.1)")
23+
tui := flag.Bool("tui", false, "full-screen dashboard mode")
24+
showVersion := flag.Bool("version", false, "print version and exit")
25+
flag.Usage = usage
26+
flag.Parse()
27+
28+
if *showVersion {
29+
fmt.Println(Version)
30+
return
31+
}
32+
33+
args := flag.Args()
34+
if len(args) != 1 {
35+
usage()
36+
os.Exit(2)
37+
}
38+
targetPort, err := strconv.Atoi(args[0])
39+
if err != nil || targetPort <= 0 || targetPort > 65535 {
40+
fmt.Fprintf(os.Stderr, "invalid target port: %q\n", args[0])
41+
os.Exit(2)
42+
}
43+
44+
extra := []string{}
45+
if *host != "" {
46+
extra = append(extra, *host)
47+
}
48+
certPath, keyPath, err := ensureCert(extra)
49+
if err != nil {
50+
fmt.Fprintln(os.Stderr, err)
51+
os.Exit(1)
52+
}
53+
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
54+
if err != nil {
55+
fmt.Fprintf(os.Stderr, "load cert: %v\n", err)
56+
os.Exit(1)
57+
}
58+
59+
bindHost := "127.0.0.1"
60+
if *lan {
61+
bindHost = "0.0.0.0"
62+
}
63+
listenAddr := fmt.Sprintf("%s:%d", bindHost, *listen)
64+
events := make(chan Event, eventBuffer)
65+
handler := newProxyHandler(targetPort, events)
66+
67+
srv := &http.Server{
68+
Addr: listenAddr,
69+
Handler: handler,
70+
TLSConfig: &tls.Config{Certificates: []tls.Certificate{cert}, MinVersion: tls.VersionTLS12},
71+
}
72+
73+
uiDone := make(chan struct{})
74+
go func() {
75+
defer close(uiDone)
76+
if *tui {
77+
runTUI(fmt.Sprintf("localhost:%d", *listen), fmt.Sprintf("http://localhost:%d", targetPort), events)
78+
} else {
79+
runLogUI(os.Stderr, fmt.Sprintf("localhost:%d", *listen), fmt.Sprintf("http://localhost:%d", targetPort), events)
80+
}
81+
}()
82+
83+
sigCh := make(chan os.Signal, 1)
84+
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
85+
86+
serveErr := make(chan error, 1)
87+
go func() {
88+
serveErr <- srv.ListenAndServeTLS("", "")
89+
}()
90+
91+
select {
92+
case err := <-serveErr:
93+
if errors.Is(err, http.ErrServerClosed) {
94+
break
95+
}
96+
fmt.Fprintf(os.Stderr, "server error: %v\n", err)
97+
close(events)
98+
<-uiDone
99+
os.Exit(1)
100+
case <-sigCh:
101+
}
102+
103+
shutdownCtx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
104+
defer cancel()
105+
_ = srv.Shutdown(shutdownCtx)
106+
close(events)
107+
<-uiDone
108+
}
109+
110+
func usage() {
111+
fmt.Fprintln(os.Stderr, `Usage: httpsdev <target-port> [flags]
112+
113+
httpsdev 5173
114+
Proxy https://localhost:3443 → http://localhost:5173
115+
116+
Flags:`)
117+
flag.PrintDefaults()
118+
}

ui_tui.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package main
2+
3+
// runTUI is implemented in Task 11.
4+
func runTUI(listenAddr, upstream string, events <-chan Event) {
5+
// Fallback for now: just drain the channel silently.
6+
for range events {
7+
}
8+
_ = listenAddr
9+
_ = upstream
10+
}

0 commit comments

Comments
 (0)