Skip to content

Commit 2941c6e

Browse files
chore:add rds connect
1 parent 02fdc62 commit 2941c6e

File tree

3 files changed

+130
-22
lines changed

3 files changed

+130
-22
lines changed

volcengine-java-sdk-core/src/main/java/com/volcengine/endpoint/DefaultEndpointProvider.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,23 @@ private static boolean hasEnabledDualstack(Boolean useDualStack) {
836836
return useDualStack;
837837
}
838838

839+
/**
840+
* Build a regional endpoint for the given service and region.
841+
* <p>
842+
* Format: {standardized_service}.{region}.volcengineapi.com
843+
* e.g., rds_mysql -> rds-mysql.cn-beijing.volcengineapi.com
844+
* <p>
845+
* Supports dual-stack via VOLC_ENABLE_DUALSTACK=true env var.
846+
*
847+
* @param service Service code (e.g., "rds_mysql")
848+
* @param region Region code (e.g., "cn-beijing")
849+
* @return Regional endpoint string (without scheme)
850+
*/
851+
public static String getRegionalEndpoint(String service, String region) {
852+
String endpointSuffix = hasEnabledDualstack(null) ? DUALSTACK_ENDPOINT_SUFFIX : ENDPOINT_SUFFIX;
853+
return standardizeDomainServiceCode(service) + SEPARATOR + region + endpointSuffix;
854+
}
855+
839856
@Override
840857
public ResolvedEndpoint endpointFor(ResolveEndpointOption option) {
841858
String endpoint = DefaultEndpointProvider.getDefaultEndpointByServiceInfo(option.getService(),

volcengine-java-sdk-core/src/main/java/com/volcengine/feature/rds/auth/ConnectUtils.java

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.volcengine.feature.rds.auth;
22

3+
import com.volcengine.ApiClient;
4+
import com.volcengine.endpoint.DefaultEndpointProvider;
35
import com.volcengine.sign.Credentials;
46
import com.volcengine.sign.VolcstackSign;
57

@@ -9,7 +11,8 @@
911
import java.util.*;
1012

1113
/**
12-
* RDS MySQL database connection utility class
14+
* RDS MySQL database connection utility class.
15+
* Generates a presigned URL that can be used as the authentication token (password) for database connections.
1316
*/
1417
public class ConnectUtils {
1518

@@ -19,13 +22,38 @@ public class ConnectUtils {
1922
private static final int DEFAULT_EXPIRES_SECONDS = 900; // 15 minutes
2023

2124
/**
22-
* Generate an authorization token for database connection (used as password)
25+
* Generate an authorization token for database connection (used as password).
26+
* Extracts credentials, region, and disableSSL from the ApiClient, similar to Go SDK's Config.
27+
*
28+
* @param apiClient ApiClient containing Credentials, Region, and DisableSSL settings
29+
* @param dbUser Database user account
30+
* @param instanceId Database instance ID
31+
* @param expires Expiration time in seconds, defaults to 900 seconds (15 minutes) if &lt;= 0
32+
* @return Presigned URL string that can be used as the authorization token for database connection
33+
* @throws Exception if parameters are invalid or signing fails
34+
*/
35+
public static String buildAuthToken(
36+
ApiClient apiClient,
37+
String dbUser,
38+
String instanceId,
39+
int expires
40+
) throws Exception {
41+
if (apiClient == null) {
42+
throw new IllegalArgumentException("apiClient must not be null");
43+
}
44+
return buildAuthToken(apiClient.getCredentials(), apiClient.getRegion(),
45+
dbUser, instanceId, expires, apiClient.getDisableSSL());
46+
}
47+
48+
/**
49+
* Generate an authorization token for database connection (used as password).
50+
* Uses HTTPS by default.
2351
*
2452
* @param credentials Credentials containing AccessKey and SecretKey for signing
2553
* @param region Region, e.g., "cn-beijing"
2654
* @param dbUser Database user account
2755
* @param instanceId Database instance ID
28-
* @param expires Expiration time in seconds, defaults to 900 seconds (15 minutes) if <= 0
56+
* @param expires Expiration time in seconds, defaults to 900 seconds (15 minutes) if &lt;= 0
2957
* @return Presigned URL string that can be used as the authorization token for database connection
3058
* @throws Exception if parameters are invalid or signing fails
3159
*/
@@ -35,6 +63,29 @@ public static String buildAuthToken(
3563
String dbUser,
3664
String instanceId,
3765
int expires
66+
) throws Exception {
67+
return buildAuthToken(credentials, region, dbUser, instanceId, expires, false);
68+
}
69+
70+
/**
71+
* Generate an authorization token for database connection (used as password).
72+
*
73+
* @param credentials Credentials containing AccessKey and SecretKey for signing
74+
* @param region Region, e.g., "cn-beijing"
75+
* @param dbUser Database user account
76+
* @param instanceId Database instance ID
77+
* @param expires Expiration time in seconds, defaults to 900 seconds (15 minutes) if &lt;= 0
78+
* @param disableSSL If true, use http:// scheme; otherwise use https://
79+
* @return Presigned URL string that can be used as the authorization token for database connection
80+
* @throws Exception if parameters are invalid or signing fails
81+
*/
82+
public static String buildAuthToken(
83+
Credentials credentials,
84+
String region,
85+
String dbUser,
86+
String instanceId,
87+
int expires,
88+
boolean disableSSL
3889
) throws Exception {
3990
// Parameter validation
4091
if (credentials == null ||
@@ -47,15 +98,27 @@ public static String buildAuthToken(
4798
throw new IllegalArgumentException("region must not be empty");
4899
}
49100

50-
if (StringUtils.isEmpty(dbUser) || StringUtils.isEmpty(instanceId)) {
51-
throw new IllegalArgumentException("dbUser or instanceId must not be empty");
101+
if (StringUtils.isEmpty(dbUser)) {
102+
throw new IllegalArgumentException("dbUser must not be empty");
103+
}
104+
105+
if (StringUtils.isEmpty(instanceId)) {
106+
throw new IllegalArgumentException("instanceId must not be empty");
52107
}
53108

109+
// Use default expires if <= 0
110+
if (expires <= 0) {
111+
expires = DEFAULT_EXPIRES_SECONDS;
112+
}
113+
114+
// Build regional endpoint
115+
String endpoint = DefaultEndpointProvider.getRegionalEndpoint(SERVICE_NAME, region);
116+
54117
// Build query parameters
55118
Map<String, String> queryParams = new HashMap<>();
56119
queryParams.put("Action", ACTION);
57120
queryParams.put("Version", VERSION);
58-
queryParams.put("X-Expires", expires > 0 ? String.valueOf(expires) : String.valueOf(DEFAULT_EXPIRES_SECONDS));
121+
queryParams.put("X-Expires", String.valueOf(expires));
59122
queryParams.put("DBUser", dbUser);
60123
queryParams.put("InstanceId", instanceId);
61124

@@ -65,21 +128,26 @@ public static String buildAuthToken(
65128
sign.setService(SERVICE_NAME);
66129
sign.setMethod("GET");
67130

68-
// Generate presigned URL
69-
Map<String, String> presignedParams = sign.presign(queryParams);
131+
// Generate presigned URL with host signing
132+
Map<String, String> presignedParams = sign.presign(queryParams, endpoint);
70133

71134
// Build complete URL
72-
return buildUrl(presignedParams);
135+
String scheme = disableSSL ? "http" : "https";
136+
return buildUrl(scheme, endpoint, presignedParams);
73137
}
74138

75139
/**
76-
* Build complete URL
140+
* Build complete URL with scheme, host, and query parameters.
77141
*
142+
* @param scheme URL scheme ("http" or "https")
143+
* @param endpoint Host endpoint (e.g., "rds-mysql.cn-beijing.volcengineapi.com")
78144
* @param presignedParams Presigned query parameters
79145
* @return Complete URL string
80146
*/
81-
private static String buildUrl(Map<String, String> presignedParams) {
147+
private static String buildUrl(String scheme, String endpoint, Map<String, String> presignedParams) {
82148
StringBuilder url = new StringBuilder();
149+
url.append(scheme).append("://").append(endpoint).append("?");
150+
83151
// Sort parameter keys
84152
List<String> keys = new ArrayList<>(presignedParams.keySet());
85153
Collections.sort(keys);

volcengine-java-sdk-core/src/main/java/com/volcengine/sign/VolcstackSign.java

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -327,12 +327,24 @@ public VolcstackSign copy() {
327327

328328

329329
/**
330-
* Generate presigned URL query parameters
330+
* Generate presigned URL query parameters (without host signing)
331331
*
332332
* @param queryParams Original query parameters
333333
* @return Complete query parameters Map containing all signature information
334334
*/
335335
public Map<String, String> presign(Map<String, String> queryParams) throws Exception {
336+
return presign(queryParams, null);
337+
}
338+
339+
/**
340+
* Generate presigned URL query parameters
341+
*
342+
* @param queryParams Original query parameters
343+
* @param host Host header value to include in signing (e.g., "rds-mysql.cn-beijing.volcengineapi.com").
344+
* If null or empty, no host header is signed.
345+
* @return Complete query parameters Map containing all signature information
346+
*/
347+
public Map<String, String> presign(Map<String, String> queryParams, String host) throws Exception {
336348
Map<String, String> presignedParams = new HashMap<>(queryParams);
337349

338350
// Generate timestamp
@@ -344,12 +356,15 @@ public Map<String, String> presign(Map<String, String> queryParams) throws Excep
344356
// Build credential scope
345357
String credentialScope = dateStamp + "/" + region + "/" + service + "/request";
346358

359+
// Determine if host header should be signed
360+
boolean signHost = host != null && !host.isEmpty();
361+
347362
// Add required query parameters for presigning
348363
presignedParams.put("X-Date", xDate);
349364
presignedParams.put("X-NotSignBody", "");
350365
presignedParams.put("X-Credential", credentials.getAccessKey() + "/" + credentialScope);
351366
presignedParams.put("X-Algorithm", "HMAC-SHA256");
352-
presignedParams.put("X-SignedHeaders", "");
367+
presignedParams.put("X-SignedHeaders", signHost ? "host" : "");
353368
presignedParams.put("X-SignedQueries", ""); // Set to empty first, will be updated later
354369

355370
// Collect all query parameter keys (excluding X-Security-Token), sort and update X-SignedQueries
@@ -384,15 +399,23 @@ public Map<String, String> presign(Map<String, String> queryParams) throws Excep
384399
canonicalRequest.append(canonicalQueryString.substring(0, canonicalQueryString.length() - 1));
385400
canonicalRequest.append("\n");
386401

387-
// Canonical Headers - empty (because X-SignedHeaders is empty)
388-
canonicalRequest.append("\n");
389-
390-
// Signed Headers - empty
391-
canonicalRequest.append("");
392-
canonicalRequest.append("\n");
393-
394-
// Extra newline before Payload Hash (required by volcstack presigning specification)
395-
canonicalRequest.append("\n");
402+
// Canonical Headers and Signed Headers
403+
if (signHost) {
404+
// Canonical Headers - include host header
405+
canonicalRequest.append("host:").append(host).append("\n");
406+
canonicalRequest.append("\n");
407+
// Signed Headers
408+
canonicalRequest.append("host");
409+
canonicalRequest.append("\n");
410+
} else {
411+
// Canonical Headers - empty
412+
canonicalRequest.append("\n");
413+
// Signed Headers - empty
414+
canonicalRequest.append("");
415+
canonicalRequest.append("\n");
416+
// Extra newline (required by volcstack presigning specification when no headers signed)
417+
canonicalRequest.append("\n");
418+
}
396419

397420
// Payload Hash - use hash value of X-NotSignBody (empty string)
398421
canonicalRequest.append(getSHA256(""));

0 commit comments

Comments
 (0)