mirror of
https://github.com/gogrlx/nats-server.git
synced 2026-04-02 03:38:42 -07:00
Add websocket browser_redirect_url config option
If an HTTP(S) request is received without the Upgrade: header, then if this new option is set then we'll assume it's a browser and issue a redirect. There's no flexibility based upon the passed in URL; this should help with redirecting people to dashboards or other tooling.
This commit is contained in:
@@ -263,6 +263,12 @@ type WebsocketOpts struct {
|
||||
// The host:port to advertise to websocket clients in the cluster.
|
||||
Advertise string
|
||||
|
||||
// If someone visits the NATS server with a browser, they will send a GET
|
||||
// without the Upgrade header.
|
||||
// For convenience, you can supply a URL to be used as an HTTP redirect,
|
||||
// instead of erroring out.
|
||||
BrowserRedirectURL string
|
||||
|
||||
// If no user name is provided when a client connects, will default to the
|
||||
// matching user from the global list of users in `Options.Users`.
|
||||
NoAuthUser string
|
||||
@@ -3453,6 +3459,8 @@ func parseWebsocket(v interface{}, o *Options, errors *[]error, warnings *[]erro
|
||||
o.Websocket.JWTCookie = mv.(string)
|
||||
case "no_auth_user":
|
||||
o.Websocket.NoAuthUser = mv.(string)
|
||||
case "browser_redirect_url":
|
||||
o.Websocket.BrowserRedirectURL = mv.(string)
|
||||
default:
|
||||
if !tk.IsUsedVariable() {
|
||||
err := &unknownConfigFieldErr{
|
||||
|
||||
@@ -529,8 +529,14 @@ func (s *Server) wsUpgrade(w http.ResponseWriter, r *http.Request) (*wsUpgradeRe
|
||||
}
|
||||
// Point 3.
|
||||
if !wsHeaderContains(r.Header, "Upgrade", "websocket") {
|
||||
_, haveUpgrade := r.Header["Upgrade"]
|
||||
if s.opts.Websocket.BrowserRedirectURL != "" && !haveUpgrade {
|
||||
// We still have to return an error, to prevent the connection upgrade.
|
||||
return nil, wsHTTPRedirectAndReturnError(w, s.opts.Websocket.BrowserRedirectURL)
|
||||
} else {
|
||||
return nil, wsReturnHTTPError(w, http.StatusBadRequest, "invalid value for header 'Upgrade'")
|
||||
}
|
||||
}
|
||||
// Point 4.
|
||||
if !wsHeaderContains(r.Header, "Connection", "Upgrade") {
|
||||
return nil, wsReturnHTTPError(w, http.StatusBadRequest, "invalid value for header 'Connection'")
|
||||
@@ -645,6 +651,15 @@ func wsReturnHTTPError(w http.ResponseWriter, status int, reason string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Send a redirect to a browser and return an error
|
||||
func wsHTTPRedirectAndReturnError(w http.ResponseWriter, redirectURL string) error {
|
||||
err := fmt.Errorf("not a websocket request, issued redirect")
|
||||
w.Header().Set("Sec-Websocket-Version", "13")
|
||||
w.Header().Set("Location", redirectURL)
|
||||
http.Error(w, http.StatusText(http.StatusFound), http.StatusFound)
|
||||
return err
|
||||
}
|
||||
|
||||
// If the server is configured to accept any origin, then this function returns
|
||||
// `nil` without checking if the Origin is present and valid.
|
||||
// Otherwise, this will check that the Origin matches the same origine or
|
||||
@@ -761,6 +776,13 @@ func validateWebsocketOptions(o *Options) error {
|
||||
return fmt.Errorf("trusted operators or trusted keys configuration is required for JWT authentication via cookie %q", wo.JWTCookie)
|
||||
}
|
||||
}
|
||||
// Ensure that we're not given random junk which might have us do
|
||||
// non-sensical things later.
|
||||
if wo.BrowserRedirectURL != "" {
|
||||
if _, err := url.Parse(wo.BrowserRedirectURL); err != nil {
|
||||
return fmt.Errorf("unable to parse get redirect URL: %v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user