Skip to content

Commit

Permalink
Added TLS support (#8)
Browse files Browse the repository at this point in the history
Sometimes there might be need to enable https while debugging.

Changes:
* Added new flag `tlsCertPath`
* Added new flag `tlsKeyPath`
* If both `tlsCertPath` and `tlsKeyPath` flags are set, `wd-41` will attempt to start a https server
  • Loading branch information
baalimago authored Oct 10, 2024
1 parent b41badb commit 82f34e9
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 8 deletions.
19 changes: 16 additions & 3 deletions cmd/serve/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ type command struct {
fileserver Fileserver

cacheControl *string
tlsCertPath *string
tlsKeyPath *string
}

func Command() *command {
Expand All @@ -55,7 +57,8 @@ func (c *command) Setup() error {
c.masterPath = path.Clean(relPath)

if c.masterPath != "" {
c.fileserver = wsinject.NewFileServer(*c.port, *c.wsPath, *c.forceReload)
expectTLS := *c.tlsCertPath != "" && *c.tlsKeyPath != ""
c.fileserver = wsinject.NewFileServer(*c.port, *c.wsPath, *c.forceReload, expectTLS)
mirrorPath, err := c.fileserver.Setup(c.masterPath)
if err != nil {
return fmt.Errorf("failed to setup websocket injected mirror filesystem: %v", err)
Expand Down Expand Up @@ -84,8 +87,16 @@ func (c *command) Run(ctx context.Context) error {
serverErrChan := make(chan error, 1)
fsErrChan := make(chan error, 1)
go func() {
ancli.PrintfOK("now serving directory: '%v' on port: '%v', mirror dir is: '%v'", c.masterPath, *c.port, c.mirrorPath)
err := s.ListenAndServe()
serveTLS := *c.tlsCertPath != "" && *c.tlsKeyPath != ""

ancli.PrintfOK("now serving directory: '%v' on port: '%v', mirror dir is: '%v', tls: %v, certPath: '%v', keyPath: '%v'",
c.masterPath, *c.port, c.mirrorPath, serveTLS, *c.tlsCertPath, *c.tlsKeyPath)
var err error
if serveTLS {
err = s.ListenAndServeTLS(*c.tlsCertPath, *c.tlsKeyPath)
} else {
err = s.ListenAndServe()
}
if !errors.Is(err, http.ErrServerClosed) {
serverErrChan <- err
}
Expand Down Expand Up @@ -127,6 +138,8 @@ func (c *command) Flagset() *flag.FlagSet {
c.wsPath = fs.String("wsPort", "/delta-streamer-ws", "the path which the delta streamer websocket should be hosted on")
c.forceReload = fs.Bool("forceReload", false, "set to true if you wish to reload all attached browser pages on any file change")
c.cacheControl = fs.String("cacheControl", "no-cache", "set to configure the cache-control header")
c.tlsCertPath = fs.String("tlsCertPath", "", "set to a path to a cert, requires tlsKeyPath to be set")
c.tlsKeyPath = fs.String("tlsKeyPath", "", "set to a path to a key, requires tlsCertPath to be set")
c.flagset = fs
return fs
}
60 changes: 60 additions & 0 deletions cmd/serve/serve_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package serve

import (
"context"
"crypto/tls"
"fmt"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -166,4 +167,63 @@ func TestRun(t *testing.T) {
got := resp.Header.Get("Cache-Control")
testboil.FailTestIfDiff(t, got, want)
})

t.Run("it should serve with tls if cert and key is specified", func(t *testing.T) {
cmd := setup()
ctx, ctxCancel := context.WithCancel(context.Background())
t.Cleanup(ctxCancel)
testCert := testboil.CreateTestFile(t, "cert.pem")
testCert.Write([]byte(`-----BEGIN CERTIFICATE-----
MIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw
DgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d
7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B
5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr
BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1
NDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l
Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc
6MF9+Yw1Yy0t
-----END CERTIFICATE-----`))
testKey := testboil.CreateTestFile(t, "key.pem")
testKey.Write([]byte(`-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIIrYSSNQFaA2Hwf1duRSxKtLYX5CB04fSeQ6tF1aY/PuoAoGCCqGSM49
AwEHoUQDQgAEPR3tU2Fta9ktY+6P9G0cWO+0kETA6SFs38GecTyudlHz6xvCdz8q
EKTcWGekdmdDPsHloRNtsiCa697B2O9IFA==
-----END EC PRIVATE KEY-----`))
port := 13337
cmd.port = &port
certPath := testCert.Name()
cmd.tlsCertPath = &certPath
keyPath := testKey.Name()
cmd.tlsKeyPath = &keyPath

ready := make(chan struct{})
go func() {
close(ready)
err := cmd.Run(ctx)
if err != nil {
t.Errorf("Run returned error: %v", err)
}
}()
<-ready
time.Sleep(time.Millisecond)

// Cert above expired in 2018
transport := &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
},
}

client := &http.Client{
Transport: transport,
}
resp, err := client.Get(fmt.Sprintf("https://localhost:%v", port))
if err != nil {
t.Fatal(err)
}
if resp.StatusCode != http.StatusOK {
t.Fatalf("expected status code: %v", resp.StatusCode)
}
})
}
2 changes: 1 addition & 1 deletion internal/wsinject/delta_streamer.ws.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function startWebsocket() {
}
// Establish a connection with the WebSocket server
const socket = new WebSocket('ws://localhost:%v%v');
const socket = new WebSocket('ws%v://localhost:%v%v');
// Event handler for when the WebSocket connection is established
socket.addEventListener('open', function (event) {
Expand Down
10 changes: 8 additions & 2 deletions internal/wsinject/wsinject.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type Fileserver struct {
masterPath string
mirrorPath string
forceReload bool
expectTLS bool
wsPort int
wsPath string
watcher *fsnotify.Watcher
Expand All @@ -37,7 +38,7 @@ var ErrNoHeaderTagFound = errors.New("no header tag found")
const deltaStreamer = `<!-- This script has been injected by wd-41 and allows hot reloads -->
<script type="module" src="delta-streamer.js"></script>`

func NewFileServer(wsPort int, wsPath string, forceReload bool) *Fileserver {
func NewFileServer(wsPort int, wsPath string, forceReload, expectTLS bool) *Fileserver {
mirrorDir, err := os.MkdirTemp("", "wd-41_*")
if err != nil {
panic(err)
Expand All @@ -47,6 +48,7 @@ func NewFileServer(wsPort int, wsPath string, forceReload bool) *Fileserver {
mirrorPath: mirrorDir,
wsPort: wsPort,
wsPath: wsPath,
expectTLS: expectTLS,
forceReload: forceReload,
pageReloadChan: make(chan string),
wsDispatcher: sync.Map{},
Expand Down Expand Up @@ -97,9 +99,13 @@ func (fs *Fileserver) mirrorMaker(p string, info os.DirEntry, err error) error {
}

func (fs *Fileserver) writeDeltaStreamerScript() error {
tlsS := ""
if fs.expectTLS {
tlsS = "s"
}
err := os.WriteFile(
path.Join(fs.mirrorPath, "delta-streamer.js"),
[]byte(fmt.Sprintf(deltaStreamerSourceCode, fs.wsPort, fs.wsPath, fs.forceReload)),
[]byte(fmt.Sprintf(deltaStreamerSourceCode, tlsS, fs.wsPort, fs.wsPath, fs.forceReload)),
0o755)
if err != nil {
return fmt.Errorf("failed to write delta-streamer.js: %w", err)
Expand Down
4 changes: 2 additions & 2 deletions internal/wsinject/wsinject_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func Test_Setup(t *testing.T) {
}
nestedFile := path.Join(nestedDir, "nested.html")
os.WriteFile(nestedFile, []byte(mockHtml), 0o777)
fs := NewFileServer(8080, "/delta-streamer-ws.js", false)
fs := NewFileServer(8080, "/delta-streamer-ws.js", false, false)
_, err = fs.Setup(tmpDir)
if err != nil {
t.Fatalf("failed to setup: %v", err)
Expand Down Expand Up @@ -144,7 +144,7 @@ func Test_Start(t *testing.T) {
if err != nil {
t.Fatalf("failed to create temp dir: %v", err)
}
return NewFileServer(8080, "/delta-streamer-ws.js", false), testFileSystem{
return NewFileServer(8080, "/delta-streamer-ws.js", false, false), testFileSystem{
root: tmpDir,
nestedDir: nestedDir,
}
Expand Down

0 comments on commit 82f34e9

Please sign in to comment.