Skip to content

feat: add switch http(s) button to error page #12942

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

Merged
merged 12 commits into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
it works
  • Loading branch information
f0ssel committed Apr 26, 2024
commit d8620eaf55d46bba692f2987a8574c579e87aeab
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ endef
# calling this manually.
$(CODER_ALL_BINARIES): go.mod go.sum \
$(GO_SRC_FILES) \
$(shell find ./examples/templates)
$(shell find ./examples/templates) \
site/static/error.html

$(get-mode-os-arch-ext)
if [[ "$$os" != "windows" ]] && [[ "$$ext" != "" ]]; then
Expand Down
31 changes: 20 additions & 11 deletions coderd/tailnet.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import (
"github.com/coder/coder/v2/coderd/tracing"
"github.com/coder/coder/v2/coderd/workspaceapps"
"github.com/coder/coder/v2/coderd/workspaceapps/appurl"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/codersdk/workspacesdk"
"github.com/coder/coder/v2/site"
"github.com/coder/coder/v2/tailnet"
Expand Down Expand Up @@ -345,7 +344,7 @@ type ServerTailnet struct {
totalConns *prometheus.CounterVec
}

func (s *ServerTailnet) ReverseProxy(targetURL, dashboardURL *url.URL, agentID uuid.UUID, app appurl.ApplicationURL) *httputil.ReverseProxy {
func (s *ServerTailnet) ReverseProxy(targetURL, dashboardURL *url.URL, agentID uuid.UUID, app appurl.ApplicationURL, wildcardHostname string) *httputil.ReverseProxy {
// Rewrite the targetURL's Host to point to the agent's IP. This is
// necessary because due to TCP connection caching, each agent needs to be
// addressed invidivually. Otherwise, all connections get dialed as
Expand All @@ -356,31 +355,41 @@ func (s *ServerTailnet) ReverseProxy(targetURL, dashboardURL *url.URL, agentID u

proxy := httputil.NewSingleHostReverseProxy(&tgt)
proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, theErr error) {
desc := "Failed to proxy request to application: " + theErr.Error()
additional := ""
var (
desc = "Failed to proxy request to application: " + theErr.Error()
additional = ""
switchLink = ""
switchTarget = ""
)

if strings.Contains(theErr.Error(), "tls:") {
// If the error is due to an HTTP/HTTPS mismatch, we can provide a
// more helpful error message with redirect buttons.
switchURL := url.URL{
Scheme: dashboardURL.Scheme,
}
if app.IsPort() {
if app.Protocol() == codersdk.WorkspaceAgentPortShareProtocolHTTPS {
app.ChangePortProtocol(codersdk.WorkspaceAgentPortShareProtocolHTTP)
if app.Protocol() == "https" {
app.ChangePortProtocol("http")
} else {
app.ChangePortProtocol(codersdk.WorkspaceAgentPortShareProtocolHTTPS)
app.ChangePortProtocol("https")
}

additional += fmt.Sprintf("This error seems to be due to an app protocol mismatch, please try switching to %s.", app.Protocol())
switchURL.Host = fmt.Sprintf("%s%s", app.String(), strings.TrimPrefix(wildcardHostname, "*"))
switchLink = switchURL.String()
switchTarget = app.Protocol()
additional += fmt.Sprintf("This error seems to be due to an app protocol mismatch, try switching to %s.", strings.ToUpper(app.Protocol()))
}
}

site.RenderStaticErrorPage(w, r, site.ErrorPageData{
Status: http.StatusBadGateway,
Title: "Bad Gateway Dood",
Title: "Bad Gateway",
Description: desc,
RetryEnabled: true,
DashboardURL: dashboardURL.String(),
SwitchProtocolLink: app.String(),
SwitchProtocolTarget: string(app.Protocol()),
SwitchProtocolLink: switchLink,
SwitchProtocolTarget: strings.ToUpper(switchTarget),
AdditionalInfo: additional,
})
}
Expand Down
38 changes: 21 additions & 17 deletions coderd/workspaceapps/appurl/appurl.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"strconv"
"strings"

"github.com/coder/coder/v2/codersdk"
"golang.org/x/xerrors"
)

Expand Down Expand Up @@ -90,6 +89,7 @@ func (a ApplicationURL) IsPort() bool {
if strings.HasSuffix(a.AppSlugOrPort, "s") {
trimmed := strings.TrimSuffix(a.AppSlugOrPort, "s")
_, err := strconv.ParseInt(trimmed, 10, 64)
//nolint:gosimple
if err != nil {
return false
}
Expand All @@ -99,41 +99,45 @@ func (a ApplicationURL) IsPort() bool {

// check if port at all
_, err := strconv.ParseInt(a.AppSlugOrPort, 10, 64)
//nolint:gosimple
if err != nil {
return false
}

return true

}

func (a ApplicationURL) Protocol() codersdk.WorkspaceAgentPortShareProtocol {
func (a ApplicationURL) Protocol() string {
if strings.HasSuffix(a.AppSlugOrPort, "s") {
trimmed := strings.TrimSuffix(a.AppSlugOrPort, "s")
_, err := strconv.ParseInt(trimmed, 10, 64)
if err == nil {
return codersdk.WorkspaceAgentPortShareProtocolHTTPS
return "https"
}
}

return codersdk.WorkspaceAgentPortShareProtocolHTTP
return "http"
}

func (a ApplicationURL) ChangePortProtocol(target codersdk.WorkspaceAgentPortShareProtocol) error {
if target == codersdk.WorkspaceAgentPortShareProtocolHTTP {
if strings.HasSuffix(a.AppSlugOrPort, "s") {
trimmed := strings.TrimSuffix(a.AppSlugOrPort, "s")
_, err := strconv.ParseInt(trimmed, 10, 64)
if err != nil {
return xerrors.Errorf("invalid port: %s", a.AppSlugOrPort)
}
func (a *ApplicationURL) ChangePortProtocol(target string) {
if target == "http" {
if a.Protocol() == "http" {
return
}
trimmed := strings.TrimSuffix(a.AppSlugOrPort, "s")
_, err := strconv.ParseInt(trimmed, 10, 64)
if err == nil {
a.AppSlugOrPort = trimmed
}
} else {
if a.Protocol() == "https" {
return
}
_, err := strconv.ParseInt(a.AppSlugOrPort, 10, 64)
if err == nil {
a.AppSlugOrPort = fmt.Sprintf("%s%s", a.AppSlugOrPort, "s")
}
}

a.AppSlugOrPort = fmt.Sprintf("%s%s", a.AppSlugOrPort, "s")

return nil
}

// ParseSubdomainAppURL parses an ApplicationURL from the given subdomain. If
Expand Down
13 changes: 3 additions & 10 deletions coderd/workspaceapps/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ var nonCanonicalHeaders = map[string]string{
type AgentProvider interface {
// ReverseProxy returns an httputil.ReverseProxy for proxying HTTP requests
// to the specified agent.
ReverseProxy(targetURL, dashboardURL *url.URL, agentID uuid.UUID, app appurl.ApplicationURL) *httputil.ReverseProxy
ReverseProxy(targetURL, dashboardURL *url.URL, agentID uuid.UUID, app appurl.ApplicationURL, wildcardHost string) *httputil.ReverseProxy

// AgentConn returns a new connection to the specified agent.
AgentConn(ctx context.Context, agentID uuid.UUID) (_ *workspacesdk.AgentConn, release func(), _ error)
Expand Down Expand Up @@ -545,16 +545,9 @@ func (s *Server) proxyWorkspaceApp(rw http.ResponseWriter, r *http.Request, appT

r.URL.Path = path
appURL.RawQuery = ""
appURL.Scheme = app.Protocol()

appURL.Scheme = "http"
if strings.HasSuffix(app.AppSlugOrPort, "s") {
_, err = strconv.ParseInt(strings.TrimSuffix(app.AppSlugOrPort, "s"), 10, 64)
if err == nil {
appURL.Scheme = "https"
}
}

proxy := s.AgentProvider.ReverseProxy(appURL, s.DashboardURL, appToken.AgentID, app)
proxy := s.AgentProvider.ReverseProxy(appURL, s.DashboardURL, appToken.AgentID, app, s.Hostname)

proxy.ModifyResponse = func(r *http.Response) error {
r.Header.Del(httpmw.AccessControlAllowOriginHeader)
Expand Down
2 changes: 1 addition & 1 deletion site/site.go
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,7 @@ type ErrorPageData struct {
DashboardURL string
Warnings []string
SwitchProtocolLink string
SwitchProtocolTarget codersdk.WorkspaceAgentPortShareProtocol
SwitchProtocolTarget string
AdditionalInfo string

RenderDescriptionMarkdown bool
Expand Down
4 changes: 2 additions & 2 deletions site/static/error.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
.container {
--side-padding: 24px;
width: 100%;
max-width: calc(320px + var(--side-padding) * 2);
max-width: calc(460px + var(--side-padding) * 2);
padding: 0 var(--side-padding);
text-align: center;
}
Expand Down Expand Up @@ -203,7 +203,7 @@ <h3>Warnings</h3>
<button onclick="window.location.reload()">Retry</button>
{{ end }}
{{- if .Error.SwitchProtocolLink }}
<a href="{{ .Error.SwitchProtocolLink }}">Switch to {{ .ErrorSwitchProtocolTarget }}</a>
<a href="{{ .Error.SwitchProtocolLink }}">Switch to {{ .Error.SwitchProtocolTarget }}</a>
{{ end }}
<a href="{{ .Error.DashboardURL }}">Back to site</a>
</div>
Expand Down