(targetURL, dashboardURL *url.URL, agentID uuid.UUID, app appurl.ApplicationURL, wildcardHostname string)
| 187 | } |
| 188 | |
| 189 | func (s *ServerTailnet) ReverseProxy(targetURL, dashboardURL *url.URL, agentID uuid.UUID, app appurl.ApplicationURL, wildcardHostname string) *httputil.ReverseProxy { |
| 190 | // Rewrite the targetURL's Host to point to the agent's IP. This is |
| 191 | // necessary because due to TCP connection caching, each agent needs to be |
| 192 | // addressed invidivually. Otherwise, all connections get dialed as |
| 193 | // "localhost:port", causing connections to be shared across agents. |
| 194 | tgt := *targetURL |
| 195 | _, port, _ := net.SplitHostPort(tgt.Host) |
| 196 | tgt.Host = net.JoinHostPort(tailnet.TailscaleServicePrefix.AddrFromUUID(agentID).String(), port) |
| 197 | |
| 198 | proxy := httputil.NewSingleHostReverseProxy(&tgt) |
| 199 | proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, theErr error) { |
| 200 | var ( |
| 201 | desc = "Failed to proxy request to application: " + theErr.Error() |
| 202 | additionalInfo = "" |
| 203 | actions = []site.Action{} |
| 204 | ) |
| 205 | |
| 206 | var tlsError tls.RecordHeaderError |
| 207 | if (errors.As(theErr, &tlsError) && tlsError.Msg == "first record does not look like a TLS handshake") || |
| 208 | errors.Is(theErr, http.ErrSchemeMismatch) { |
| 209 | // If the error is due to an HTTP/HTTPS mismatch, we can provide a |
| 210 | // more helpful error message with redirect buttons. |
| 211 | switchURL := url.URL{ |
| 212 | Scheme: dashboardURL.Scheme, |
| 213 | } |
| 214 | _, protocol, isPort := app.PortInfo() |
| 215 | if isPort { |
| 216 | targetProtocol := "https" |
| 217 | if protocol == "https" { |
| 218 | targetProtocol = "http" |
| 219 | } |
| 220 | app = app.ChangePortProtocol(targetProtocol) |
| 221 | |
| 222 | switchURL.Host = fmt.Sprintf("%s%s", app.String(), strings.TrimPrefix(wildcardHostname, "*")) |
| 223 | actions = append(actions, site.Action{ |
| 224 | URL: switchURL.String(), |
| 225 | Text: fmt.Sprintf("Switch to %s", strings.ToUpper(targetProtocol)), |
| 226 | }) |
| 227 | additionalInfo += fmt.Sprintf("This error seems to be due to an app protocol mismatch, try switching to %s.", strings.ToUpper(targetProtocol)) |
| 228 | } |
| 229 | } |
| 230 | |
| 231 | site.RenderStaticErrorPage(w, r, site.ErrorPageData{ |
| 232 | Status: http.StatusBadGateway, |
| 233 | Title: "Bad Gateway", |
| 234 | Description: desc, |
| 235 | Actions: append(actions, []site.Action{ |
| 236 | { |
| 237 | Text: "Retry", |
| 238 | }, |
| 239 | { |
| 240 | URL: dashboardURL.String(), |
| 241 | Text: "Back to site", |
| 242 | }, |
| 243 | }...), |
| 244 | AdditionalInfo: additionalInfo, |
| 245 | }) |
| 246 | } |
nothing calls this directly
no test coverage detected