@@ -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