diff options
| author | Jeroen Wijenbergh <jeroen.wijenbergh@geant.org> | 2025-08-29 14:36:00 +0200 |
|---|---|---|
| committer | Jeroen Wijenbergh <jeroen.wijenbergh@geant.org> | 2025-08-29 14:40:25 +0200 |
| commit | 132782f44603dfdc3b1d875d632f786109ee09a2 (patch) | |
| tree | 71cd1cefee78636167c560c80c8bc79c8982fe26 /internal/discovery/discovery.go | |
| parent | 4a23134e1e5d70a9c8c5857790dbf27585ca3b1f (diff) | |
Discovery: Remove manager and DiscoveryStartup
Diffstat (limited to 'internal/discovery/discovery.go')
| -rw-r--r-- | internal/discovery/discovery.go | 59 |
1 files changed, 36 insertions, 23 deletions
diff --git a/internal/discovery/discovery.go b/internal/discovery/discovery.go index 80a69eb..512756b 100644 --- a/internal/discovery/discovery.go +++ b/internal/discovery/discovery.go @@ -8,6 +8,7 @@ import ( "fmt" "log/slog" "net/http" + "sync" "time" httpw "codeberg.org/eduVPN/eduvpn-common/internal/http" @@ -83,6 +84,8 @@ func (s *Server) Score(search string) int { // Discovery is the main structure used for this package. type Discovery struct { + // mu is the read write mutex that protects the struct from concurrent access + mu sync.RWMutex // The httpClient for sending HTTP requests httpClient *httpw.Client @@ -174,23 +177,27 @@ func (discovery *Discovery) file(ctx context.Context, jsonFile string, previousV // MarkOrganizationsExpired marks the organizations as expired func (discovery *Discovery) MarkOrganizationsExpired() { + discovery.mu.Lock() + defer discovery.mu.Unlock() // Re-initialize the timestamp to zero discovery.OrganizationList.Timestamp = time.Time{} } // MarkServersExpired marks the servers as expired func (discovery *Discovery) MarkServersExpired() { + discovery.mu.Lock() + defer discovery.mu.Unlock() // Re-initialize the timestamp to zero discovery.ServerList.Timestamp = time.Time{} } -// DetermineOrganizationsUpdate returns a boolean indicating whether or not the discovery organizations should be updated +// determineOrganizationsUpdate returns a boolean indicating whether or not the discovery organizations should be updated // https://github.com/eduvpn/documentation/blob/v3/SERVER_DISCOVERY.md // - [IMPLEMENTED] on "first launch" when offering the search for "Institute Access" and "Organizations"; // - [IMPLEMENTED in client/client.go and here] when the user tries to add new server AND the user did NOT yet choose an organization before; Implemented in Register() // - [IMPLEMENTED in client/client.go] when the authorization for the server associated with an already chosen organization is triggered, e.g. after expiry or revocation. // - [IMPLEMENTED here] NOTE: when the org_id that the user chose previously is no longer available in organization_list.json the application should ask the user to choose their organization (again). This can occur for example when the organization replaced their identity provider, uses a different domain after rebranding or simply ceased to exist. -func (discovery *Discovery) DetermineOrganizationsUpdate() bool { +func (discovery *Discovery) determineOrganizationsUpdate() bool { if discovery.OrganizationList.Timestamp.IsZero() { return true } @@ -202,6 +209,8 @@ func (discovery *Discovery) DetermineOrganizationsUpdate() bool { // SecureLocationList returns a slice of all the available locations. func (discovery *Discovery) SecureLocationList() []string { + discovery.mu.RLock() + defer discovery.mu.RUnlock() var loc []string for _, srv := range discovery.ServerList.List { if srv.Type == "secure_internet" { @@ -217,6 +226,8 @@ func (discovery *Discovery) ServerByURL( baseURL string, srvType string, ) (*Server, error) { + discovery.mu.RLock() + defer discovery.mu.RUnlock() for _, currentServer := range discovery.ServerList.List { if currentServer.BaseURL == baseURL && currentServer.Type == srvType { return ¤tServer, nil @@ -237,6 +248,8 @@ func (cnf *ErrCountryNotFound) Error() string { // ServerByCountryCode returns the discovery server by the country code // An error is returned if and only if nil is returned for the server. func (discovery *Discovery) ServerByCountryCode(countryCode string) (*Server, error) { + discovery.mu.RLock() + defer discovery.mu.RUnlock() for _, srv := range discovery.ServerList.List { if srv.CountryCode == countryCode && srv.Type == "secure_internet" { return &srv, nil @@ -261,8 +274,10 @@ func (discovery *Discovery) orgByID(orgID string) (*Organization, error) { // - The secure internet server itself // An error is returned if and only if nil is returned for the organization. func (discovery *Discovery) SecureHomeArgs(orgID string) (*Organization, *Server, error) { + discovery.mu.RLock() org, err := discovery.orgByID(orgID) if err != nil { + discovery.mu.RUnlock() discovery.MarkOrganizationsExpired() return nil, nil, err } @@ -270,17 +285,19 @@ func (discovery *Discovery) SecureHomeArgs(orgID string) (*Organization, *Server // Get a server with the base url srv, err := discovery.ServerByURL(org.SecureInternetHome, "secure_internet") if err != nil { + discovery.mu.RUnlock() discovery.MarkOrganizationsExpired() return nil, nil, err } + discovery.mu.RUnlock() return org, srv, nil } -// DetermineServersUpdate returns whether or not the discovery servers should be updated by contacting the discovery server +// determineServersUpdate returns whether or not the discovery servers should be updated by contacting the discovery server // https://github.com/eduvpn/documentation/blob/v3/SERVER_DISCOVERY.md // - [Implemented] The application MUST always fetch the server_list.json at application start. // - The application MAY refresh the server_list.json periodically, e.g. once every hour. -func (discovery *Discovery) DetermineServersUpdate() bool { +func (discovery *Discovery) determineServersUpdate() bool { // No servers, we should update if discovery.ServerList.Timestamp.IsZero() { return true @@ -305,9 +322,14 @@ func (discovery *Discovery) cachedOrgs() *Organizations { // If there was an error, a cached copy is returned if available. // cache is set to true if there should be no network call done func (discovery *Discovery) Organizations(ctx context.Context, cache bool) (*Organizations, bool, error) { - if cache || !discovery.DetermineOrganizationsUpdate() { + discovery.mu.RLock() + if cache || !discovery.determineOrganizationsUpdate() { + discovery.mu.RUnlock() return discovery.cachedOrgs(), false, nil } + discovery.mu.RUnlock() + discovery.mu.Lock() + defer discovery.mu.Unlock() file := "organization_list.json" var jsonDecode Organizations update, err := discovery.file(ctx, file, discovery.OrganizationList.Version, discovery.OrganizationList.UpdateHeader, &jsonDecode) @@ -349,9 +371,14 @@ func (discovery *Discovery) cachedServers() *Servers { // If there was an error, a cached copy is returned if available. // cache is set to true if there should be no network call done func (discovery *Discovery) Servers(ctx context.Context, cache bool) (*Servers, bool, error) { - if cache || !discovery.DetermineServersUpdate() { + discovery.mu.RLock() + if cache || !discovery.determineServersUpdate() { + discovery.mu.RUnlock() return discovery.cachedServers(), false, nil } + discovery.mu.RUnlock() + discovery.mu.Lock() + defer discovery.mu.Unlock() file := "server_list.json" var jsonDecode Servers update, err := discovery.file(ctx, file, discovery.ServerList.Version, discovery.ServerList.UpdateHeader, &jsonDecode) @@ -397,29 +424,15 @@ func (discovery *Discovery) UpdateOrganizations(other Organizations) { } } -// Copy creates a deep-copy for the discovery struct -// It does this by marshalling and unmarshalling it as JSON -func (discovery *Discovery) Copy() (Discovery, error) { - var dest Discovery - b, err := json.Marshal(discovery) - if err != nil { - return dest, err - } - - err = json.Unmarshal(b, &dest) - if err != nil { - return dest, err - } - - return dest, nil -} - // Fill makes sure that the cache is filled with the embedded discovery func (discovery *Discovery) Fill() error { if !HasCache { return nil } + discovery.mu.Lock() + defer discovery.mu.Unlock() + var err error var es Servers err = errors.Join(err, json.Unmarshal(eServers, &es)) |
