Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

golink: listen on HTTPS and redirect HTTP traffic #99

Merged
merged 10 commits into from
Dec 18, 2023
83 changes: 77 additions & 6 deletions golink.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"bytes"
"context"
"crypto/rand"
"crypto/tls"
"embed"
"encoding/base64"
"encoding/json"
Expand Down Expand Up @@ -169,18 +170,78 @@ func Run() error {
if err := srv.Start(); err != nil {
return err
}
localClient, _ = srv.LocalClient()

l80, err := srv.Listen("tcp", ":80")
// create tsNet server and wait for it to be ready & connected.
localClient, _ = srv.LocalClient()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we're here, maybe now is a good time to

localClient, err = srv.LocalClient()
if err != nil {
  return err
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As long as the server is started, LocalClient promises not to report an error in this context.

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
_, err = srv.Up(ctx)
if err != nil {
return err
}

log.Printf("Serving http://%s/ ...", *hostname)
if err := http.Serve(l80, serveHandler()); err != nil {
return err
enableTLS := len(srv.CertDomains()) > 0
if enableTLS {
// warm the certificate cache for all cert domains to prevent users waiting
// on ACME challenges in-line on their first request.
for _, d := range srv.CertDomains() {
log.Printf("Provisioning TLS certificate for %s ...", d)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()

_, _, err := localClient.CertPair(ctx, d)
if err != nil {
return err
}
}

redirectFqdn := srv.CertDomains()[0]
// HTTP listener that redirects to our HTTPS listener.
log.Println("Listening on :80")
httpListener, err := srv.Listen("tcp", ":80")
if err != nil {
return err
}
go func() error {
log.Printf("Serving http://%s/ ...", *hostname)
if err := http.Serve(httpListener, redirectHandler(redirectFqdn)); err != nil {
return err
}
return nil
}()

log.Println("Listening on :443")
httpsListener, err := srv.Listen("tcp", ":443")
if err != nil {
return err
}
s := http.Server{
Addr: ":443",
Handler: serveHandler(),
TLSConfig: &tls.Config{
GetCertificate: localClient.GetCertificate,
},
}

log.Printf("Serving https://%s/\n", redirectFqdn)
if err := s.ServeTLS(httpsListener, "", ""); err != nil {
return err
}
return nil
} else {
// no TLS, just serve on :80
log.Println("Listening on :80")
httpListener, err := srv.Listen("tcp", ":80")
if err != nil {
return err
}
log.Printf("Serving http://%s/ ...", *hostname)
if err := http.Serve(httpListener, serveHandler()); err != nil {
return err
}
return nil
}
return nil

}

var (
Expand Down Expand Up @@ -286,6 +347,16 @@ func deleteLinkStats(link *Link) {
db.DeleteStats(link.Short)
}

// redirectHandler returns the http.Handler for serving all plaintext HTTP
// requests. It redirects all requests to the HTTPs version of the same URL.
func redirectHandler(hostname string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
newUrl := fmt.Sprintf("https://%s%s", hostname, path)
http.Redirect(w, r, newUrl, http.StatusMovedPermanently)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not entirely sure this works, I vaguely recall something about go/foo not working once you start doing redirects.

})
}

// serverHandler returns the main http.Handler for serving all requests.
func serveHandler() http.Handler {
mux := http.NewServeMux()
Expand Down
4 changes: 2 additions & 2 deletions tmpl/help.html
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,15 @@ <h2 id="api">Application Programming Interface (API)</h2>
Visit <a href="/.export">go/.export</a> to export all saved links and their metadata in <a href="https://jsonlines.org/">JSON Lines format</a>.
This is useful to create data snapshots that can be restored later.

<pre>{{`$ curl go/.export
<pre>{{`$ curl -L go/.export
{"Short":"go","Long":"http://go","Created":"2022-05-31T13:04:44.741457796-07:00","LastEdit":"2022-05-31T13:04:44.741457796-07:00","Owner":"[email protected]","Clicks":1}
{"Short":"slack","Long":"https://company.slack.com/{{if .Path}}channels/{{PathEscape .Path}}{{end}}","Created":"2022-06-17T18:05:43.562948451Z","LastEdit":"2022-06-17T18:06:35.811398Z","Owner":"[email protected]","Clicks":4}`}}
</pre>

<p>
Create a new link by sending a POST request with a <code>short</code> and <code>long</code> value:

<pre>{{`$ curl -d short=cs -d long=https://cs.github.com/ go
<pre>{{`$ curl -L -d short=cs -d long=https://cs.github.com/ go
{"Short":"cs","Long":"https://cs.github.com/","Created":"2022-06-03T22:15:29.993978392Z","LastEdit":"2022-06-03T22:15:29.993978392Z","Owner":"[email protected]"}`}}
</pre>

Expand Down