@@ -40,6 +40,8 @@ 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" ;
4345 static const char DEFAULTS_MODE_KEY[] = " defaults_mode" ;
4446 static const char EQ = ' =' ;
4547 static const char LEFT_BRACKET = ' [' ;
@@ -74,6 +76,7 @@ namespace Aws
7476 {EXTERNAL_ID_KEY, &Profile::SetExternalId, &Profile::GetExternalId},
7577 {CREDENTIAL_PROCESS_COMMAND, &Profile::SetCredentialProcess, &Profile::GetCredentialProcess},
7678 {SOURCE_PROFILE_KEY, &Profile::SetSourceProfile, &Profile::GetSourceProfile},
79+ {ENDPOINT_URL_KEY, &Profile::SetEndpointUrl, &Profile::GetEndpointUrl},
7780 {DEFAULTS_MODE_KEY, &Profile::SetDefaultsMode, &Profile::GetDefaultsMode}};
7881
7982 template <typename EntryT, size_t N>
@@ -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 currentServiceId;
122126 Aws::Map<Aws::String, Aws::String> currentKeyValues;
123127
124128 Aws::String rawLine;
@@ -140,13 +144,14 @@ namespace Aws
140144
141145 if (openPos != std::string::npos && closePos != std::string::npos)
142146 {
143- FlushSection (currentState, currentSectionName, currentKeyValues);
147+ FlushSection (currentState, currentSectionName, currentServiceId, currentKeyValues);
144148 currentKeyValues.clear ();
145- ParseSectionDeclaration (line, currentSectionName, currentState);
149+ currentServiceId.clear ();
150+ ParseSectionDeclaration (line, currentSectionName, currentServiceId, currentState);
146151 continue ;
147152 }
148153
149- if (PROFILE_FOUND == currentState || SSO_SESSION_FOUND == currentState)
154+ if (PROFILE_FOUND == currentState || SSO_SESSION_FOUND == currentState || SERVICES_SERVICE_FOUND == currentState )
150155 {
151156 auto equalsPos = line.find (EQ);
152157 if (equalsPos != std::string::npos)
@@ -158,6 +163,16 @@ namespace Aws
158163 }
159164 }
160165
166+ if (SERVICES_FOUND == currentState)
167+ {
168+ // Skip key-value pairs in [services] section without service ID
169+ auto equalsPos = line.find (EQ);
170+ if (equalsPos != std::string::npos)
171+ {
172+ continue ;
173+ }
174+ }
175+
161176 if (UNKNOWN_SECTION_FOUND == currentState)
162177 {
163178 // skip any unknown sections
@@ -169,7 +184,7 @@ namespace Aws
169184 break ;
170185 }
171186
172- FlushSection (currentState, currentSectionName, currentKeyValues);
187+ FlushSection (currentState, currentSectionName, currentServiceId, currentKeyValues);
173188
174189 // Put sso-sessions into profiles
175190 for (auto & profile : m_foundProfiles)
@@ -222,6 +237,8 @@ namespace Aws
222237 START = 0 ,
223238 PROFILE_FOUND,
224239 SSO_SESSION_FOUND,
240+ SERVICES_FOUND,
241+ SERVICES_SERVICE_FOUND,
225242 UNKNOWN_SECTION_FOUND,
226243 FAILURE
227244 };
@@ -271,12 +288,14 @@ namespace Aws
271288
272289 /* *
273290 * A helper function to parse config section declaration line
274- * @param line, an input line, e.g. "[profile default]"
291+ * @param line, an input line, e.g. "[profile default]" or "[services s3]"
275292 * @param ioSectionName, a return argument representing parsed section Identifier, e.g. "default"
293+ * @param ioServiceId, a return argument representing parsed service ID for services sections
276294 * @param ioState, a return argument representing parser state, e.g. PROFILE_FOUND
277295 */
278296 void ParseSectionDeclaration (const Aws::String& line,
279297 Aws::String& ioSectionName,
298+ Aws::String& ioServiceId,
280299 State& ioState)
281300 {
282301 do { // goto in a form of "do { break; } while(0);"
@@ -331,21 +350,21 @@ namespace Aws
331350
332351 if (defaultProfileOrSsoSectionRequired)
333352 {
334- if (sectionIdentifier != DEFAULT && sectionIdentifier != SSO_SESSION_SECTION)
353+ if (sectionIdentifier != DEFAULT && sectionIdentifier != SSO_SESSION_SECTION && sectionIdentifier != SERVICES_SECTION )
335354 {
336355 AWS_LOGSTREAM_ERROR (PARSER_TAG, " In configuration files, the profile name must start with "
337356 " profile keyword (except default profile): " << line);
338357 break ;
339358 }
340- if (sectionIdentifier != SSO_SESSION_SECTION)
359+ if (sectionIdentifier != SSO_SESSION_SECTION && sectionIdentifier != SERVICES_SECTION )
341360 {
342361 // profile found, still pending check for closing bracket
343362 ioState = PROFILE_FOUND;
344363 ioSectionName = sectionIdentifier;
345364 }
346365 }
347366
348- if (!m_useProfilePrefix || sectionIdentifier != SSO_SESSION_SECTION)
367+ if (!m_useProfilePrefix || ( sectionIdentifier != SSO_SESSION_SECTION && sectionIdentifier != SERVICES_SECTION) )
349368 {
350369 // profile found, still pending check for closing bracket
351370 ioState = PROFILE_FOUND;
@@ -374,6 +393,33 @@ namespace Aws
374393 ioSectionName = sectionIdentifier;
375394 }
376395
396+ if (sectionIdentifier == SERVICES_SECTION)
397+ {
398+ // Check if this is [services] or [services serviceId]
399+ pos = line.find_first_not_of (WHITESPACE_CHARACTERS, pos);
400+ if (pos == Aws::String::npos || line[pos] == RIGHT_BRACKET)
401+ {
402+ // This is just [services] section
403+ ioState = SERVICES_FOUND;
404+ ioSectionName = sectionIdentifier;
405+ }
406+ else
407+ {
408+ // This is [services serviceId] section
409+ sectionIdentifier = ParseIdentifier (line, pos, errorMsg);
410+ if (!errorMsg.empty ())
411+ {
412+ AWS_LOGSTREAM_ERROR (PARSER_TAG, " Failed to parse service identifier: " << errorMsg << " " << line);
413+ break ;
414+ }
415+ 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 ());
420+ }
421+ }
422+
377423 pos = line.find_first_not_of (WHITESPACE_CHARACTERS, pos);
378424 if (pos == Aws::String::npos)
379425 {
@@ -394,7 +440,7 @@ namespace Aws
394440 break ;
395441 }
396442 // the rest is a comment, and we don't care about it.
397- if ((ioState != SSO_SESSION_FOUND && ioState != PROFILE_FOUND) || ioSectionName.empty ())
443+ if ((ioState != SSO_SESSION_FOUND && ioState != PROFILE_FOUND && ioState != SERVICES_FOUND && ioState != SERVICES_SERVICE_FOUND ) || ioSectionName.empty ())
398444 {
399445 AWS_LOGSTREAM_FATAL (PARSER_TAG, " Unexpected parser state after attempting to parse section " << line);
400446 break ;
@@ -412,9 +458,10 @@ namespace Aws
412458 * (i.e. [profile default] and its key1=val1 under).
413459 * @param currentState, a current parser State, e.g. PROFILE_FOUND
414460 * @param currentSectionName, a current section identifier, e.g. "default"
461+ * @param currentServiceId, a current service identifier for services sections
415462 * @param currentKeyValues, a map of parsed key-value properties of a section definition being recorded
416463 */
417- void FlushSection (const State currentState, const Aws::String& currentSectionName, Aws::Map<Aws::String, Aws::String>& currentKeyValues)
464+ void FlushSection (const State currentState, const Aws::String& currentSectionName, const Aws::String& currentServiceId, Aws::Map<Aws::String, Aws::String>& currentKeyValues)
418465 {
419466 if (START == currentState || currentSectionName.empty ())
420467 {
@@ -529,6 +576,21 @@ namespace Aws
529576 ssoSession.SetName (currentSectionName);
530577 ssoSession.SetAllKeyValPairs (std::move (currentKeyValues));
531578 }
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 " << currentServiceId << " : " << endpointUrlIter->second );
587+ profile.SetServiceEndpointUrl (currentServiceId, endpointUrlIter->second );
588+ }
589+ }
590+ 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" );
593+ }
532594 else
533595 {
534596 AWS_LOGSTREAM_FATAL (PARSER_TAG, " Unknown parser error: unexpected state " << currentState);
@@ -665,5 +727,26 @@ namespace Aws
665727
666728 return false ;
667729 }
730+
731+ const Aws::String* AWSConfigFileProfileConfigLoader::GetServiceEndpointUrl (const Aws::String& profileName, const Aws::String& serviceId) const
732+ {
733+ auto profileIter = m_profiles.find (profileName);
734+ if (profileIter == m_profiles.end ())
735+ {
736+ return nullptr ;
737+ }
738+ return profileIter->second .GetServiceEndpointUrl (serviceId);
739+ }
740+
741+ const Aws::String* AWSConfigFileProfileConfigLoader::GetGlobalEndpointUrl (const Aws::String& profileName) const
742+ {
743+ auto profileIter = m_profiles.find (profileName);
744+ if (profileIter == m_profiles.end ())
745+ {
746+ return nullptr ;
747+ }
748+ const Aws::String& endpointUrl = profileIter->second .GetEndpointUrl ();
749+ return endpointUrl.empty () ? nullptr : &endpointUrl;
750+ }
668751 } // Config namespace
669752} // Aws namespace
0 commit comments