@@ -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 " << currentSectionName << " ]" );
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 : " << errorMsg << " " << line);
432+ AWS_LOGSTREAM_ERROR (PARSER_TAG, " Failed to parse services definition name : " << errorMsg << " " << line);
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 " << line);
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 " << currentServiceId << " : " << endpointUrlIter->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