@@ -40,6 +40,9 @@ namespace Aws
4040        static  const  char  PROFILE_SECTION[]                  = " profile"  ;
4141        static  const  char  DEFAULT[]                          = " default"  ;
4242        static  const  char  SSO_SESSION_SECTION[]              = " sso-session"  ;
43+         static  const  char  SERVICES_SECTION[]                 = " services"  ;
44+         static  const  char  ENDPOINT_URL_KEY[]                 = " endpoint_url"  ;
45+         static  const  char  IGNORE_CONFIGURED_ENDPOINT_URLS_KEY[] = " ignore_configured_endpoint_urls"  ;
4346        static  const  char  DEFAULTS_MODE_KEY[]                = " defaults_mode"  ;
4447        static  const  char  EQ                                 = ' ='  ;
4548        static  const  char  LEFT_BRACKET                       = ' ['  ;
@@ -74,7 +77,8 @@ namespace Aws
7477                 {EXTERNAL_ID_KEY, &Profile::SetExternalId, &Profile::GetExternalId},
7578                 {CREDENTIAL_PROCESS_COMMAND, &Profile::SetCredentialProcess, &Profile::GetCredentialProcess},
7679                 {SOURCE_PROFILE_KEY, &Profile::SetSourceProfile, &Profile::GetSourceProfile},
77-                  {DEFAULTS_MODE_KEY, &Profile::SetDefaultsMode, &Profile::GetDefaultsMode}};
80+                  {DEFAULTS_MODE_KEY, &Profile::SetDefaultsMode, &Profile::GetDefaultsMode},
81+                  {ENDPOINT_URL_KEY, &Profile::SetGlobalEndpointUrl, &Profile::GetGlobalEndpointUrl}};
7882
7983        template <typename  EntryT, size_t  N>
8084        const  EntryT* FindInStaticArray (const  EntryT  (&array)[N], const  Aws::String& searchKey)
@@ -119,6 +123,7 @@ namespace Aws
119123                static  const  size_t  ASSUME_EMPTY_LEN = 3 ;
120124                State currentState = START;
121125                Aws::String currentSectionName;
126+                 Aws::String activeServiceId;
122127                Aws::Map<Aws::String, Aws::String> currentKeyValues;
123128
124129                Aws::String rawLine;
@@ -142,6 +147,7 @@ namespace Aws
142147                    {
143148                        FlushSection (currentState, currentSectionName, currentKeyValues);
144149                        currentKeyValues.clear ();
150+                         activeServiceId.clear ();
145151                        ParseSectionDeclaration (line, currentSectionName, currentState);
146152                        continue ;
147153                    }
@@ -158,6 +164,36 @@ namespace Aws
158164                        }
159165                    }
160166
167+                     if (SERVICES_FOUND == currentState)
168+                     {
169+                         auto  equalsPos = line.find (EQ);
170+                         if  (equalsPos == std::string::npos) {
171+                             continue ; //  ignore garbage/blank in services section
172+                         }
173+ 
174+                         auto  left = StringUtils::Trim (line.substr (0 , equalsPos).c_str ());
175+                         auto  right = StringUtils::Trim (line.substr (equalsPos + 1 ).c_str ());
176+ 
177+                         //  New service block: "s3 =" (right hand side empty)
178+                         if  (!left.empty () && right.empty ()) {
179+                             activeServiceId = StringUtils::ToUpper (left.c_str ());
180+                             StringUtils::Replace (activeServiceId, "  "  , " _"  );
181+                             continue ;
182+                         }
183+ 
184+                         //  Ignore global endpoint_url in [services name] section
185+                         if  (activeServiceId.empty () && StringUtils::CaselessCompare (left.c_str (), ENDPOINT_URL_KEY) == 0 ) {
186+                             AWS_LOGSTREAM_DEBUG (PARSER_TAG, " Ignoring global endpoint_url in [services "   << currentSectionName << " ]"  );
187+                             continue ;
188+                         }
189+ 
190+                         //  Property inside an active block: "endpoint_url = http://..."
191+                         if  (!activeServiceId.empty () && left == ENDPOINT_URL_KEY) {
192+                             m_services[currentSectionName][activeServiceId] = right;
193+                             continue ;
194+                         }
195+                     }
196+ 
161197                    if (UNKNOWN_SECTION_FOUND == currentState)
162198                    {
163199                        //  skip any unknown sections
@@ -171,6 +207,22 @@ namespace Aws
171207
172208                FlushSection (currentState, currentSectionName, currentKeyValues);
173209
210+                 //  Resolve service endpoints
211+                 for  (auto & profilePair : m_foundProfiles)
212+                 {
213+                     Profile& profile = profilePair.second ;
214+                     const  Aws::String& servicesRef = profile.GetValue (" services"  );
215+                     if  (!servicesRef.empty ())
216+                     {
217+                         auto  servicesBlk = m_services.find (servicesRef);
218+                         Aws::Map<Aws::String, Aws::String> endpoints;
219+                         if  (servicesBlk != m_services.end ()) {
220+                             endpoints = servicesBlk->second ;
221+                         }
222+                         profile.SetServices (Profile::Services (std::move (endpoints), servicesRef));
223+                     }
224+                 }
225+ 
174226                //  Put sso-sessions into profiles
175227                for (auto & profile : m_foundProfiles)
176228                {
@@ -222,6 +274,7 @@ namespace Aws
222274                START = 0 ,
223275                PROFILE_FOUND,
224276                SSO_SESSION_FOUND,
277+                 SERVICES_FOUND,
225278                UNKNOWN_SECTION_FOUND,
226279                FAILURE
227280            };
@@ -271,8 +324,9 @@ namespace Aws
271324
272325            /* *
273326             * A helper function to parse config section declaration line 
274-              * @param line, an input line, e.g. "[profile default]" 
327+              * @param line, an input line, e.g. "[profile default]" or "[services s3]"  
275328             * @param ioSectionName, a return argument representing parsed section Identifier, e.g. "default" 
329+              * @param ioServiceId, a return argument representing parsed service ID for services sections 
276330             * @param ioState, a return argument representing parser state, e.g. PROFILE_FOUND 
277331             */  
278332            void  ParseSectionDeclaration (const  Aws::String& line,
@@ -331,21 +385,21 @@ namespace Aws
331385
332386                    if (defaultProfileOrSsoSectionRequired)
333387                    {
334-                         if  (sectionIdentifier != DEFAULT && sectionIdentifier != SSO_SESSION_SECTION)
388+                         if  (sectionIdentifier != DEFAULT && sectionIdentifier != SSO_SESSION_SECTION && sectionIdentifier != SERVICES_SECTION )
335389                        {
336390                            AWS_LOGSTREAM_ERROR (PARSER_TAG, " In configuration files, the profile name must start with " 
337391                                                            " profile keyword (except default profile): "   << line);
338392                            break ;
339393                        }
340-                         if  (sectionIdentifier != SSO_SESSION_SECTION)
394+                         if  (sectionIdentifier != SSO_SESSION_SECTION && sectionIdentifier != SERVICES_SECTION )
341395                        {
342396                            //  profile found, still pending check for closing bracket
343397                            ioState = PROFILE_FOUND;
344398                            ioSectionName = sectionIdentifier;
345399                        }
346400                    }
347401
348-                     if (!m_useProfilePrefix || sectionIdentifier != SSO_SESSION_SECTION)
402+                     if (!m_useProfilePrefix || ( sectionIdentifier != SSO_SESSION_SECTION && sectionIdentifier != SERVICES_SECTION) )
349403                    {
350404                        //  profile found, still pending check for closing bracket
351405                        ioState = PROFILE_FOUND;
@@ -374,6 +428,32 @@ namespace Aws
374428                        ioSectionName = sectionIdentifier;
375429                    }
376430
431+                     if (sectionIdentifier == SERVICES_SECTION)
432+                     {
433+                         //  Check if this is [services] or [services name]
434+                         pos = line.find_first_not_of (WHITESPACE_CHARACTERS, pos);
435+                         if (pos == Aws::String::npos || line[pos] == RIGHT_BRACKET)
436+                         {
437+                             //  This is just [services] section
438+                             AWS_LOGSTREAM_ERROR (PARSER_TAG, " [services] section without name is not supported: "   << line);
439+                             break ;
440+                         }
441+                         else 
442+                         {
443+                             //  This is [services name] section
444+                             sectionIdentifier = ParseIdentifier (line, pos, errorMsg);
445+                             if  (!errorMsg.empty ())
446+                             {
447+                                 AWS_LOGSTREAM_ERROR (PARSER_TAG, " Failed to parse services definition name: "   << errorMsg << "  "   << line);
448+                                 break ;
449+                             }
450+                             pos += sectionIdentifier.length ();
451+                             //  services definition found, still pending check for closing bracket
452+                             ioState = SERVICES_FOUND;
453+                             ioSectionName = sectionIdentifier;
454+                         }
455+                     }
456+ 
377457                    pos = line.find_first_not_of (WHITESPACE_CHARACTERS, pos);
378458                    if (pos == Aws::String::npos)
379459                    {
@@ -394,7 +474,7 @@ namespace Aws
394474                        break ;
395475                    }
396476                    //  the rest is a comment, and we don't care about it.
397-                     if  ((ioState != SSO_SESSION_FOUND && ioState != PROFILE_FOUND) || ioSectionName.empty ())
477+                     if  ((ioState != SSO_SESSION_FOUND && ioState != PROFILE_FOUND && ioState != SERVICES_FOUND ) || ioSectionName.empty ())
398478                    {
399479                        AWS_LOGSTREAM_FATAL (PARSER_TAG, " Unexpected parser state after attempting to parse section "   << line);
400480                        break ;
@@ -412,6 +492,7 @@ namespace Aws
412492             *   (i.e. [profile default] and its key1=val1 under). 
413493             * @param currentState, a current parser State, e.g. PROFILE_FOUND 
414494             * @param currentSectionName, a current section identifier, e.g. "default" 
495+              * @param currentServiceId, a current service identifier for services sections 
415496             * @param currentKeyValues, a map of parsed key-value properties of a section definition being recorded 
416497             */  
417498            void  FlushSection (const  State currentState, const  Aws::String& currentSectionName, Aws::Map<Aws::String, Aws::String>& currentKeyValues)
@@ -529,6 +610,10 @@ namespace Aws
529610                    ssoSession.SetName (currentSectionName);
530611                    ssoSession.SetAllKeyValPairs (std::move (currentKeyValues));
531612                }
613+                 else  if  (SERVICES_FOUND == currentState) {
614+                     //  Handle [services name] section - service endpoints are parsed inline during stream processing
615+                     AWS_LOGSTREAM_DEBUG (PARSER_TAG, " Processed [services "   << currentSectionName << " ] section"  );
616+                 }
532617                else 
533618                {
534619                    AWS_LOGSTREAM_FATAL (PARSER_TAG, " Unknown parser error: unexpected state "   << currentState);
@@ -557,6 +642,7 @@ namespace Aws
557642
558643            Aws::Map<String, Profile> m_foundProfiles;
559644            Aws::Map<String, Profile::SsoSession> m_foundSsoSessions;
645+             Aws::Map<String, Aws::Map<String, String>> m_services;
560646        };
561647
562648        static  const  char * const  CONFIG_FILE_LOADER = " Aws::Config::AWSConfigFileProfileConfigLoader"  ;
0 commit comments