@@ -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 = ' [' ;
@@ -119,6 +122,7 @@ namespace Aws
119122 static const size_t ASSUME_EMPTY_LEN = 3 ;
120123 State currentState = START;
121124 Aws::String currentSectionName;
125+ Aws::String activeServiceId;
122126 Aws::Map<Aws::String, Aws::String> currentKeyValues;
123127
124128 Aws::String rawLine;
@@ -142,6 +146,7 @@ namespace Aws
142146 {
143147 FlushSection (currentState, currentSectionName, currentKeyValues);
144148 currentKeyValues.clear ();
149+ activeServiceId.clear ();
145150 ParseSectionDeclaration (line, currentSectionName, currentState);
146151 continue ;
147152 }
@@ -158,6 +163,36 @@ namespace Aws
158163 }
159164 }
160165
166+ if (SERVICES_FOUND == currentState)
167+ {
168+ auto equalsPos = line.find (EQ);
169+ if (equalsPos == std::string::npos) {
170+ continue ; // ignore garbage/blank in services section
171+ }
172+
173+ auto left = StringUtils::Trim (line.substr (0 , equalsPos).c_str ());
174+ auto right = StringUtils::Trim (line.substr (equalsPos + 1 ).c_str ());
175+
176+ // New service block: "s3 =" (right hand side empty)
177+ if (!left.empty () && right.empty ()) {
178+ activeServiceId = StringUtils::ToUpper (left.c_str ());
179+ StringUtils::Replace (activeServiceId, " " , " _" );
180+ continue ;
181+ }
182+
183+ // Ignore global endpoint_url in [services name] section
184+ if (activeServiceId.empty () && StringUtils::CaselessCompare (left.c_str (), ENDPOINT_URL_KEY) == 0 ) {
185+ AWS_LOGSTREAM_DEBUG (PARSER_TAG, " Ignoring global endpoint_url in [services " << currentSectionName << " ]" );
186+ continue ;
187+ }
188+
189+ // Property inside an active block: "endpoint_url = http://..."
190+ if (!activeServiceId.empty () && left == ENDPOINT_URL_KEY) {
191+ m_services[currentSectionName][activeServiceId] = right;
192+ continue ;
193+ }
194+ }
195+
161196 if (UNKNOWN_SECTION_FOUND == currentState)
162197 {
163198 // skip any unknown sections
@@ -171,6 +206,22 @@ namespace Aws
171206
172207 FlushSection (currentState, currentSectionName, currentKeyValues);
173208
209+ // Resolve service endpoints
210+ for (auto & profilePair : m_foundProfiles)
211+ {
212+ Profile& profile = profilePair.second ;
213+ const Aws::String& servicesRef = profile.GetValue (" services" );
214+ if (!servicesRef.empty ())
215+ {
216+ auto servicesBlk = m_services.find (servicesRef);
217+ Aws::Map<Aws::String, Aws::String> endpoints;
218+ if (servicesBlk != m_services.end ()) {
219+ endpoints = servicesBlk->second ;
220+ }
221+ profile.SetServices (Profile::Services (std::move (endpoints), servicesRef));
222+ }
223+ }
224+
174225 // Put sso-sessions into profiles
175226 for (auto & profile : m_foundProfiles)
176227 {
@@ -222,6 +273,7 @@ namespace Aws
222273 START = 0 ,
223274 PROFILE_FOUND,
224275 SSO_SESSION_FOUND,
276+ SERVICES_FOUND,
225277 UNKNOWN_SECTION_FOUND,
226278 FAILURE
227279 };
@@ -271,8 +323,9 @@ namespace Aws
271323
272324 /* *
273325 * A helper function to parse config section declaration line
274- * @param line, an input line, e.g. "[profile default]"
326+ * @param line, an input line, e.g. "[profile default]" or "[services s3]"
275327 * @param ioSectionName, a return argument representing parsed section Identifier, e.g. "default"
328+ * @param ioServiceId, a return argument representing parsed service ID for services sections
276329 * @param ioState, a return argument representing parser state, e.g. PROFILE_FOUND
277330 */
278331 void ParseSectionDeclaration (const Aws::String& line,
@@ -331,21 +384,21 @@ namespace Aws
331384
332385 if (defaultProfileOrSsoSectionRequired)
333386 {
334- if (sectionIdentifier != DEFAULT && sectionIdentifier != SSO_SESSION_SECTION)
387+ if (sectionIdentifier != DEFAULT && sectionIdentifier != SSO_SESSION_SECTION && sectionIdentifier != SERVICES_SECTION )
335388 {
336389 AWS_LOGSTREAM_ERROR (PARSER_TAG, " In configuration files, the profile name must start with "
337390 " profile keyword (except default profile): " << line);
338391 break ;
339392 }
340- if (sectionIdentifier != SSO_SESSION_SECTION)
393+ if (sectionIdentifier != SSO_SESSION_SECTION && sectionIdentifier != SERVICES_SECTION )
341394 {
342395 // profile found, still pending check for closing bracket
343396 ioState = PROFILE_FOUND;
344397 ioSectionName = sectionIdentifier;
345398 }
346399 }
347400
348- if (!m_useProfilePrefix || sectionIdentifier != SSO_SESSION_SECTION)
401+ if (!m_useProfilePrefix || ( sectionIdentifier != SSO_SESSION_SECTION && sectionIdentifier != SERVICES_SECTION) )
349402 {
350403 // profile found, still pending check for closing bracket
351404 ioState = PROFILE_FOUND;
@@ -374,6 +427,32 @@ namespace Aws
374427 ioSectionName = sectionIdentifier;
375428 }
376429
430+ if (sectionIdentifier == SERVICES_SECTION)
431+ {
432+ // Check if this is [services] or [services name]
433+ pos = line.find_first_not_of (WHITESPACE_CHARACTERS, pos);
434+ if (pos == Aws::String::npos || line[pos] == RIGHT_BRACKET)
435+ {
436+ // This is just [services] section
437+ AWS_LOGSTREAM_ERROR (PARSER_TAG, " [services] section without name is not supported: " << line);
438+ break ;
439+ }
440+ else
441+ {
442+ // This is [services name] section
443+ sectionIdentifier = ParseIdentifier (line, pos, errorMsg);
444+ if (!errorMsg.empty ())
445+ {
446+ AWS_LOGSTREAM_ERROR (PARSER_TAG, " Failed to parse services definition name: " << errorMsg << " " << line);
447+ break ;
448+ }
449+ pos += sectionIdentifier.length ();
450+ // services definition found, still pending check for closing bracket
451+ ioState = SERVICES_FOUND;
452+ ioSectionName = sectionIdentifier;
453+ }
454+ }
455+
377456 pos = line.find_first_not_of (WHITESPACE_CHARACTERS, pos);
378457 if (pos == Aws::String::npos)
379458 {
@@ -394,7 +473,7 @@ namespace Aws
394473 break ;
395474 }
396475 // the rest is a comment, and we don't care about it.
397- if ((ioState != SSO_SESSION_FOUND && ioState != PROFILE_FOUND) || ioSectionName.empty ())
476+ if ((ioState != SSO_SESSION_FOUND && ioState != PROFILE_FOUND && ioState != SERVICES_FOUND ) || ioSectionName.empty ())
398477 {
399478 AWS_LOGSTREAM_FATAL (PARSER_TAG, " Unexpected parser state after attempting to parse section " << line);
400479 break ;
@@ -412,6 +491,7 @@ namespace Aws
412491 * (i.e. [profile default] and its key1=val1 under).
413492 * @param currentState, a current parser State, e.g. PROFILE_FOUND
414493 * @param currentSectionName, a current section identifier, e.g. "default"
494+ * @param currentServiceId, a current service identifier for services sections
415495 * @param currentKeyValues, a map of parsed key-value properties of a section definition being recorded
416496 */
417497 void FlushSection (const State currentState, const Aws::String& currentSectionName, Aws::Map<Aws::String, Aws::String>& currentKeyValues)
@@ -529,6 +609,10 @@ namespace Aws
529609 ssoSession.SetName (currentSectionName);
530610 ssoSession.SetAllKeyValPairs (std::move (currentKeyValues));
531611 }
612+ else if (SERVICES_FOUND == currentState) {
613+ // Handle [services name] section - service endpoints are parsed inline during stream processing
614+ AWS_LOGSTREAM_DEBUG (PARSER_TAG, " Processed [services " << currentSectionName << " ] section" );
615+ }
532616 else
533617 {
534618 AWS_LOGSTREAM_FATAL (PARSER_TAG, " Unknown parser error: unexpected state " << currentState);
@@ -557,6 +641,7 @@ namespace Aws
557641
558642 Aws::Map<String, Profile> m_foundProfiles;
559643 Aws::Map<String, Profile::SsoSession> m_foundSsoSessions;
644+ Aws::Map<String, Aws::Map<String, String>> m_services;
560645 };
561646
562647 static const char * const CONFIG_FILE_LOADER = " Aws::Config::AWSConfigFileProfileConfigLoader" ;
0 commit comments