Browse Source

api, gui: Prevent connection issues due to unsupported-upgrade (fixes #8569) (#8586)

There are some situations where an upgrade wouldn't be supported, even though the noUpgrade bool isn't set. So when handling the errors that are caused by this, when attempting an upgrade, it shouldn't lead to some sort of offline-message/restart/warning/etc...

I added some checks on specific errors related to this and return a 501 (Not Implemented) response instead, in case of an "UpgradeUnsupported"-error. Additionally, on the GUI-side, the 501-response is now not to be considered an error to act upon.
Eric P 3 years ago
parent
commit
c791dba392
2 changed files with 30 additions and 22 deletions
  1. 3 2
      gui/default/syncthing/core/syncthingController.js
  2. 27 20
      lib/api/api.go

+ 3 - 2
gui/default/syncthing/core/syncthingController.js

@@ -183,8 +183,9 @@ angular.module('syncthing.core')
                 if (arg.status === 0) {
                 if (arg.status === 0) {
                     // A network error, not an HTTP error
                     // A network error, not an HTTP error
                     $scope.$emit(Events.OFFLINE);
                     $scope.$emit(Events.OFFLINE);
-                } else if (arg.status >= 400 && arg.status <= 599) {
-                    // A genuine HTTP error
+                } else if (arg.status >= 400 && arg.status <= 599 && arg.status != 501) {
+                    // A genuine HTTP error. 501/NotImplemented is considered intentional 
+                    // and not an error which we need to act upon.
                     $('#networkError').modal('hide');
                     $('#networkError').modal('hide');
                     $('#restarting').modal('hide');
                     $('#restarting').modal('hide');
                     $('#shutdown').modal('hide');
                     $('#shutdown').modal('hide');

+ 27 - 20
lib/api/api.go

@@ -864,7 +864,7 @@ func (s *service) getDBRemoteNeed(w http.ResponseWriter, r *http.Request) {
 	device := qs.Get("device")
 	device := qs.Get("device")
 	deviceID, err := protocol.DeviceIDFromString(device)
 	deviceID, err := protocol.DeviceIDFromString(device)
 	if err != nil {
 	if err != nil {
-		http.Error(w, err.Error(), 500)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 		return
 	}
 	}
 
 
@@ -1017,7 +1017,7 @@ func (s *service) postSystemReset(w http.ResponseWriter, r *http.Request) {
 
 
 	if len(folder) > 0 {
 	if len(folder) > 0 {
 		if _, ok := s.cfg.Folders()[folder]; !ok {
 		if _, ok := s.cfg.Folders()[folder]; !ok {
-			http.Error(w, "Invalid folder ID", 500)
+			http.Error(w, "Invalid folder ID", http.StatusInternalServerError)
 			return
 			return
 		}
 		}
 	}
 	}
@@ -1291,7 +1291,7 @@ func (s *service) getReport(w http.ResponseWriter, r *http.Request) {
 		version = val
 		version = val
 	}
 	}
 	if r, err := s.urService.ReportDataPreview(context.TODO(), version); err != nil {
 	if r, err := s.urService.ReportDataPreview(context.TODO(), version); err != nil {
-		http.Error(w, err.Error(), 500)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 		return
 	} else {
 	} else {
 		sendJSON(w, r)
 		sendJSON(w, r)
@@ -1316,7 +1316,7 @@ func (s *service) getDBIgnores(w http.ResponseWriter, r *http.Request) {
 
 
 	lines, patterns, err := s.model.LoadIgnores(folder)
 	lines, patterns, err := s.model.LoadIgnores(folder)
 	if err != nil && !ignore.IsParseError(err) {
 	if err != nil && !ignore.IsParseError(err) {
-		http.Error(w, err.Error(), 500)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 		return
 	}
 	}
 
 
@@ -1333,20 +1333,20 @@ func (s *service) postDBIgnores(w http.ResponseWriter, r *http.Request) {
 	bs, err := io.ReadAll(r.Body)
 	bs, err := io.ReadAll(r.Body)
 	r.Body.Close()
 	r.Body.Close()
 	if err != nil {
 	if err != nil {
-		http.Error(w, err.Error(), 500)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 		return
 	}
 	}
 
 
 	var data map[string][]string
 	var data map[string][]string
 	err = json.Unmarshal(bs, &data)
 	err = json.Unmarshal(bs, &data)
 	if err != nil {
 	if err != nil {
-		http.Error(w, err.Error(), 500)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 		return
 	}
 	}
 
 
 	err = s.model.SetIgnores(qs.Get("folder"), data["ignore"])
 	err = s.model.SetIgnores(qs.Get("folder"), data["ignore"])
 	if err != nil {
 	if err != nil {
-		http.Error(w, err.Error(), 500)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 		return
 	}
 	}
 
 
@@ -1424,13 +1424,13 @@ func (s *service) getEventSub(mask events.EventType) events.BufferedSubscription
 
 
 func (s *service) getSystemUpgrade(w http.ResponseWriter, _ *http.Request) {
 func (s *service) getSystemUpgrade(w http.ResponseWriter, _ *http.Request) {
 	if s.noUpgrade {
 	if s.noUpgrade {
-		http.Error(w, upgrade.ErrUpgradeUnsupported.Error(), http.StatusServiceUnavailable)
+		http.Error(w, upgrade.ErrUpgradeUnsupported.Error(), http.StatusNotImplemented)
 		return
 		return
 	}
 	}
 	opts := s.cfg.Options()
 	opts := s.cfg.Options()
 	rel, err := upgrade.LatestRelease(opts.ReleasesURL, build.Version, opts.UpgradeToPreReleases)
 	rel, err := upgrade.LatestRelease(opts.ReleasesURL, build.Version, opts.UpgradeToPreReleases)
 	if err != nil {
 	if err != nil {
-		http.Error(w, err.Error(), 500)
+		httpError(w, err)
 		return
 		return
 	}
 	}
 	res := make(map[string]interface{})
 	res := make(map[string]interface{})
@@ -1472,8 +1472,7 @@ func (s *service) postSystemUpgrade(w http.ResponseWriter, _ *http.Request) {
 	opts := s.cfg.Options()
 	opts := s.cfg.Options()
 	rel, err := upgrade.LatestRelease(opts.ReleasesURL, build.Version, opts.UpgradeToPreReleases)
 	rel, err := upgrade.LatestRelease(opts.ReleasesURL, build.Version, opts.UpgradeToPreReleases)
 	if err != nil {
 	if err != nil {
-		l.Warnln("getting latest release:", err)
-		http.Error(w, err.Error(), 500)
+		httpError(w, err)
 		return
 		return
 	}
 	}
 
 
@@ -1481,7 +1480,7 @@ func (s *service) postSystemUpgrade(w http.ResponseWriter, _ *http.Request) {
 		err = upgrade.To(rel)
 		err = upgrade.To(rel)
 		if err != nil {
 		if err != nil {
 			l.Warnln("upgrading:", err)
 			l.Warnln("upgrading:", err)
-			http.Error(w, err.Error(), 500)
+			http.Error(w, err.Error(), http.StatusInternalServerError)
 			return
 			return
 		}
 		}
 
 
@@ -1528,7 +1527,7 @@ func (s *service) makeDevicePauseHandler(paused bool) http.HandlerFunc {
 		if msg != "" {
 		if msg != "" {
 			http.Error(w, msg, status)
 			http.Error(w, msg, status)
 		} else if err != nil {
 		} else if err != nil {
-			http.Error(w, err.Error(), 500)
+			http.Error(w, err.Error(), http.StatusInternalServerError)
 		}
 		}
 	}
 	}
 }
 }
@@ -1540,7 +1539,7 @@ func (s *service) postDBScan(w http.ResponseWriter, r *http.Request) {
 		subs := qs["sub"]
 		subs := qs["sub"]
 		err := s.model.ScanFolderSubdirs(folder, subs)
 		err := s.model.ScanFolderSubdirs(folder, subs)
 		if err != nil {
 		if err != nil {
-			http.Error(w, err.Error(), 500)
+			http.Error(w, err.Error(), http.StatusInternalServerError)
 			return
 			return
 		}
 		}
 		nextStr := qs.Get("next")
 		nextStr := qs.Get("next")
@@ -1551,7 +1550,7 @@ func (s *service) postDBScan(w http.ResponseWriter, r *http.Request) {
 	} else {
 	} else {
 		errors := s.model.ScanFolders()
 		errors := s.model.ScanFolders()
 		if len(errors) > 0 {
 		if len(errors) > 0 {
-			http.Error(w, "Error scanning folders", 500)
+			http.Error(w, "Error scanning folders", http.StatusInternalServerError)
 			sendJSON(w, errors)
 			sendJSON(w, errors)
 			return
 			return
 		}
 		}
@@ -1571,7 +1570,7 @@ func (*service) getQR(w http.ResponseWriter, r *http.Request) {
 	var text = qs.Get("text")
 	var text = qs.Get("text")
 	code, err := qr.Encode(text, qr.M)
 	code, err := qr.Encode(text, qr.M)
 	if err != nil {
 	if err != nil {
-		http.Error(w, "Invalid", 500)
+		http.Error(w, "Invalid", http.StatusInternalServerError)
 		return
 		return
 	}
 	}
 
 
@@ -1612,7 +1611,7 @@ func (s *service) getFolderVersions(w http.ResponseWriter, r *http.Request) {
 	qs := r.URL.Query()
 	qs := r.URL.Query()
 	versions, err := s.model.GetFolderVersions(qs.Get("folder"))
 	versions, err := s.model.GetFolderVersions(qs.Get("folder"))
 	if err != nil {
 	if err != nil {
-		http.Error(w, err.Error(), 500)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 		return
 	}
 	}
 	sendJSON(w, versions)
 	sendJSON(w, versions)
@@ -1624,20 +1623,20 @@ func (s *service) postFolderVersionsRestore(w http.ResponseWriter, r *http.Reque
 	bs, err := io.ReadAll(r.Body)
 	bs, err := io.ReadAll(r.Body)
 	r.Body.Close()
 	r.Body.Close()
 	if err != nil {
 	if err != nil {
-		http.Error(w, err.Error(), 500)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 		return
 	}
 	}
 
 
 	var versions map[string]time.Time
 	var versions map[string]time.Time
 	err = json.Unmarshal(bs, &versions)
 	err = json.Unmarshal(bs, &versions)
 	if err != nil {
 	if err != nil {
-		http.Error(w, err.Error(), 500)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 		return
 	}
 	}
 
 
 	ferr, err := s.model.RestoreFolderVersions(qs.Get("folder"), versions)
 	ferr, err := s.model.RestoreFolderVersions(qs.Get("folder"), versions)
 	if err != nil {
 	if err != nil {
-		http.Error(w, err.Error(), 500)
+		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 		return
 	}
 	}
 	sendJSON(w, errorStringMap(ferr))
 	sendJSON(w, errorStringMap(ferr))
@@ -2015,3 +2014,11 @@ func isFolderNotFound(err error) bool {
 	}
 	}
 	return false
 	return false
 }
 }
+
+func httpError(w http.ResponseWriter, err error) {
+	if errors.Is(err, upgrade.ErrUpgradeUnsupported) {
+		http.Error(w, upgrade.ErrUpgradeUnsupported.Error(), http.StatusNotImplemented)
+	} else {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+	}
+}