@@ -76,6 +76,7 @@ namespace Aws
7676                 {EXTERNAL_ID_KEY, &Profile::SetExternalId, &Profile::GetExternalId},
7777                 {CREDENTIAL_PROCESS_COMMAND, &Profile::SetCredentialProcess, &Profile::GetCredentialProcess},
7878                 {SOURCE_PROFILE_KEY, &Profile::SetSourceProfile, &Profile::GetSourceProfile},
79+                  {SERVICES_SECTION, &Profile::SetServicesDefinitionName, &Profile::GetServicesDefinitionName},
7980                 {ENDPOINT_URL_KEY, &Profile::SetEndpointUrl, &Profile::GetEndpointUrl},
8081                 {DEFAULTS_MODE_KEY, &Profile::SetDefaultsMode, &Profile::GetDefaultsMode}};
8182
@@ -116,13 +117,14 @@ namespace Aws
116117            {}
117118
118119            const  Aws::Map<String, Profile>& GetProfiles () const  { return  m_foundProfiles; }
120+             const  Aws::Map<String, Aws::Map<String, String>>& GetServicesDefinitions () const  { return  m_servicesDefinitions; }
119121
120122            void  ParseStream (Aws::IStream& stream)
121123            {
122124                static  const  size_t  ASSUME_EMPTY_LEN = 3 ;
123125                State currentState = START;
124126                Aws::String currentSectionName;
125-                 Aws::String currentServiceId ;
127+                 Aws::String activeServiceId ;
126128                Aws::Map<Aws::String, Aws::String> currentKeyValues;
127129
128130                Aws::String rawLine;
@@ -144,14 +146,14 @@ namespace Aws
144146
145147                    if (openPos != std::string::npos && closePos != std::string::npos)
146148                    {
147-                         FlushSection (currentState, currentSectionName, currentServiceId,  currentKeyValues);
149+                         FlushSection (currentState, currentSectionName, currentKeyValues);
148150                        currentKeyValues.clear ();
149-                         currentServiceId .clear ();
150-                         ParseSectionDeclaration (line, currentSectionName, currentServiceId,  currentState);
151+                         activeServiceId .clear ();
152+                         ParseSectionDeclaration (line, currentSectionName, currentState);
151153                        continue ;
152154                    }
153155
154-                     if (PROFILE_FOUND == currentState || SSO_SESSION_FOUND == currentState || SERVICES_SERVICE_FOUND == currentState )
156+                     if (PROFILE_FOUND == currentState || SSO_SESSION_FOUND == currentState)
155157                    {
156158                        auto  equalsPos = line.find (EQ);
157159                        if  (equalsPos != std::string::npos)
@@ -165,10 +167,30 @@ namespace Aws
165167
166168                    if (SERVICES_FOUND == currentState)
167169                    {
168-                         //  Skip key-value pairs in [services] section without service ID
169170                        auto  equalsPos = line.find (EQ);
170-                         if  (equalsPos != std::string::npos)
171-                         {
171+                         if  (equalsPos == std::string::npos) {
172+                             continue ; //  ignore garbage/blank in services section
173+                         }
174+ 
175+                         auto  left = StringUtils::Trim (line.substr (0 , equalsPos).c_str ());
176+                         auto  right = StringUtils::Trim (line.substr (equalsPos + 1 ).c_str ());
177+ 
178+                         //  New service block: "s3 =" (right hand side empty)
179+                         if  (!left.empty () && right.empty ()) {
180+                             activeServiceId = StringUtils::ToLower (left.c_str ());
181+                             StringUtils::Replace (activeServiceId, "  " " _" 
182+                             continue ;
183+                         }
184+ 
185+                         //  Ignore top-level endpoint_url in [services name] section
186+                         if  (activeServiceId.empty () && StringUtils::CaselessCompare (left.c_str (), ENDPOINT_URL_KEY) == 0 ) {
187+                             AWS_LOGSTREAM_DEBUG (PARSER_TAG, " Ignoring global endpoint_url in [services " " ]" 
188+                             continue ;
189+                         }
190+ 
191+                         //  Property inside an active block: "endpoint_url = http://..."
192+                         if  (!activeServiceId.empty () && left == ENDPOINT_URL_KEY) {
193+                             m_servicesDefinitions[currentSectionName][activeServiceId] = right;
172194                            continue ;
173195                        }
174196                    }
@@ -184,7 +206,7 @@ namespace Aws
184206                    break ;
185207                }
186208
187-                 FlushSection (currentState, currentSectionName, currentServiceId,  currentKeyValues);
209+                 FlushSection (currentState, currentSectionName, currentKeyValues);
188210
189211                //  Put sso-sessions into profiles
190212                for (auto & profile : m_foundProfiles)
@@ -238,7 +260,6 @@ namespace Aws
238260                PROFILE_FOUND,
239261                SSO_SESSION_FOUND,
240262                SERVICES_FOUND,
241-                 SERVICES_SERVICE_FOUND,
242263                UNKNOWN_SECTION_FOUND,
243264                FAILURE
244265            };
@@ -295,7 +316,6 @@ namespace Aws
295316             */  
296317            void  ParseSectionDeclaration (const  Aws::String& line,
297318                                         Aws::String& ioSectionName,
298-                                          Aws::String& ioServiceId,
299319                                         State& ioState)
300320            {
301321                do  { //  goto in a form of "do { break; } while(0);"
@@ -395,28 +415,27 @@ namespace Aws
395415
396416                    if (sectionIdentifier == SERVICES_SECTION)
397417                    {
398-                         //  Check if this is [services] or [services serviceId ]
418+                         //  Check if this is [services] or [services name ]
399419                        pos = line.find_first_not_of (WHITESPACE_CHARACTERS, pos);
400420                        if (pos == Aws::String::npos || line[pos] == RIGHT_BRACKET)
401421                        {
402422                            //  This is just [services] section
403-                             ioState = SERVICES_FOUND ;
404-                             ioSectionName = sectionIdentifier ;
423+                             AWS_LOGSTREAM_ERROR (PARSER_TAG,  " [services] section without name is not supported:  "  << line) ;
424+                             break ;
405425                        }
406426                        else 
407427                        {
408-                             //  This is [services serviceId ] section
428+                             //  This is [services name ] section
409429                            sectionIdentifier = ParseIdentifier (line, pos, errorMsg);
410430                            if  (!errorMsg.empty ())
411431                            {
412-                                 AWS_LOGSTREAM_ERROR (PARSER_TAG, " Failed to parse service identifier : " "  " 
432+                                 AWS_LOGSTREAM_ERROR (PARSER_TAG, " Failed to parse services definition name : " "  " 
413433                                break ;
414434                            }
415435                            pos += sectionIdentifier.length ();
416-                             //  services serviceId found, still pending check for closing bracket
417-                             ioState = SERVICES_SERVICE_FOUND;
418-                             ioSectionName = " default" //  Use default profile for services
419-                             ioServiceId = StringUtils::ToLower (sectionIdentifier.c_str ());
436+                             //  services definition found, still pending check for closing bracket
437+                             ioState = SERVICES_FOUND;
438+                             ioSectionName = sectionIdentifier;
420439                        }
421440                    }
422441
@@ -440,7 +459,7 @@ namespace Aws
440459                        break ;
441460                    }
442461                    //  the rest is a comment, and we don't care about it.
443-                     if  ((ioState != SSO_SESSION_FOUND && ioState != PROFILE_FOUND && ioState != SERVICES_FOUND && ioState != SERVICES_SERVICE_FOUND ) || ioSectionName.empty ())
462+                     if  ((ioState != SSO_SESSION_FOUND && ioState != PROFILE_FOUND && ioState != SERVICES_FOUND) || ioSectionName.empty ())
444463                    {
445464                        AWS_LOGSTREAM_FATAL (PARSER_TAG, " Unexpected parser state after attempting to parse section " 
446465                        break ;
@@ -461,7 +480,7 @@ namespace Aws
461480             * @param currentServiceId, a current service identifier for services sections 
462481             * @param currentKeyValues, a map of parsed key-value properties of a section definition being recorded 
463482             */  
464-             void  FlushSection (const  State currentState, const  Aws::String& currentSectionName, const  Aws::String& currentServiceId,  Aws::Map<Aws::String, Aws::String>& currentKeyValues)
483+             void  FlushSection (const  State currentState, const  Aws::String& currentSectionName, Aws::Map<Aws::String, Aws::String>& currentKeyValues)
465484            {
466485                if (START == currentState || currentSectionName.empty ())
467486                {
@@ -576,20 +595,9 @@ namespace Aws
576595                    ssoSession.SetName (currentSectionName);
577596                    ssoSession.SetAllKeyValPairs (std::move (currentKeyValues));
578597                }
579-                 else  if  (SERVICES_SERVICE_FOUND == currentState) {
580-                     //  Handle [services serviceId] section
581-                     Profile& profile = m_foundProfiles[currentSectionName];
582-                     
583-                     auto  endpointUrlIter = currentKeyValues.find (ENDPOINT_URL_KEY);
584-                     if  (endpointUrlIter != currentKeyValues.end ())
585-                     {
586-                         AWS_LOGSTREAM_DEBUG (PARSER_TAG, " Found service endpoint_url for " " : " second );
587-                         profile.SetServiceEndpointUrl (currentServiceId, endpointUrlIter->second );
588-                     }
589-                 }
590598                else  if  (SERVICES_FOUND == currentState) {
591-                     //  Handle [services] section - currently no-op as we ignore key-value pairs here 
592-                     AWS_LOGSTREAM_DEBUG (PARSER_TAG, " Processed [services] section" 
599+                     //  Handle [services name ] section - service endpoints are parsed inline during stream processing 
600+                     AWS_LOGSTREAM_DEBUG (PARSER_TAG, " Processed [services  "  << currentSectionName <<  " ] section" 
593601                }
594602                else 
595603                {
@@ -619,6 +627,7 @@ namespace Aws
619627
620628            Aws::Map<String, Profile> m_foundProfiles;
621629            Aws::Map<String, Profile::SsoSession> m_foundSsoSessions;
630+             Aws::Map<String, Aws::Map<String, String>> m_servicesDefinitions;
622631        };
623632
624633        static  const  char * const  CONFIG_FILE_LOADER = " Aws::Config::AWSConfigFileProfileConfigLoader" 
@@ -633,13 +642,15 @@ namespace Aws
633642        bool  AWSConfigFileProfileConfigLoader::LoadInternal ()
634643        {
635644            m_profiles.clear ();
645+             m_servicesDefinitions.clear ();
636646
637647            Aws::IFStream inputFile (m_fileName.c_str ());
638648            if (inputFile)
639649            {
640650                ConfigFileProfileFSM parser (m_useProfilePrefix);
641651                parser.ParseStream (inputFile);
642652                m_profiles = parser.GetProfiles ();
653+                 m_servicesDefinitions = parser.GetServicesDefinitions ();
643654                return  m_profiles.size () > 0 ;
644655            }
645656
@@ -735,7 +746,21 @@ namespace Aws
735746            {
736747                return  nullptr ;
737748            }
738-             return  profileIter->second .GetServiceEndpointUrl (serviceId);
749+             
750+             const  auto & servicesDefName = profileIter->second .GetServicesDefinitionName ();
751+             if  (servicesDefName.empty ()) {
752+                 return  nullptr ;
753+             }
754+             
755+             auto  servicesDefIter = m_servicesDefinitions.find (servicesDefName);
756+             if  (servicesDefIter == m_servicesDefinitions.end ()) {
757+                 return  nullptr ;
758+             }
759+             
760+             Aws::String key = StringUtils::ToLower (serviceId.c_str ());
761+             StringUtils::Replace (key, "  " " _" 
762+             auto  serviceIter = servicesDefIter->second .find (key);
763+             return  (serviceIter == servicesDefIter->second .end ()) ? nullptr  : &serviceIter->second ;
739764        }
740765
741766        const  Aws::String* AWSConfigFileProfileConfigLoader::GetGlobalEndpointUrl (const  Aws::String& profileName) const 
0 commit comments