diff --git a/examples/cvm/v20170312/Nginx.java b/examples/cvm/v20170312/Nginx.java new file mode 100644 index 0000000000..3a62eb53d7 --- /dev/null +++ b/examples/cvm/v20170312/Nginx.java @@ -0,0 +1,60 @@ +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.exception.TencentCloudSDKException; +import com.tencentcloudapi.common.profile.ClientProfile; +import com.tencentcloudapi.common.profile.HttpProfile; +import com.tencentcloudapi.cvm.v20170312.CvmClient; +import com.tencentcloudapi.cvm.v20170312.models.DescribeInstancesRequest; +import com.tencentcloudapi.cvm.v20170312.models.DescribeInstancesResponse; + +import java.util.HashMap; + +public class Nginx { + public static void main(String[] args) { + /* + 本示例演示如何通过 **nginx 反向代理** 请求腾讯云 API。 + 场景:某些网络环境不能直接访问腾讯云域名(*.tencentcloudapi.com),只能通过代理服务器。 + ======================== + 1. nginx 配置示例: + ======================== + server { + listen 80; + # 指定 DNS 服务器(可以根据自己网络环境进行替换) + resolver 114.114.114.114; + # 可以自定义请求路径 + location /tc_api { + # http_host 后必须以 / 结尾 + proxy_pass https://$http_host/$is_args$args; + } + } + */ + try { + Credential cred = new Credential( + System.getenv("TENCENTCLOUD_SECRET_ID"), + System.getenv("TENCENTCLOUD_SECRET_KEY") + ); + + ClientProfile cpf = new ClientProfile(); + // 2. 将 Scheme 设置为 http + cpf.getHttpProfile().setProtocol(HttpProfile.REQ_HTTP); + + // 3. 替换 1.2.3.4 为真实的 nginx 地址, /tc_api 可以自定义 + String nginx = "1.2.3.4/tc_api"; + cpf.getHttpProfile().setEndpoint(nginx); + + CvmClient client = new CvmClient(cred, "ap-guangzhou", cpf); + + DescribeInstancesRequest req = new DescribeInstancesRequest(); + // 4. 设置 header 为 {服务名}.tencentcloudapi.com + req.SetHeader(new HashMap() {{ + put("Host", "cvm.tencentcloudapi.com"); + }}); + req.setLimit(10L); + + DescribeInstancesResponse resp = client.DescribeInstances(req); + System.out.println(DescribeInstancesResponse.toJsonString(resp)); + + } catch (TencentCloudSDKException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/tencentcloudapi/common/AbstractClient.java b/src/main/java/com/tencentcloudapi/common/AbstractClient.java index 34012569b2..edb9c15226 100644 --- a/src/main/java/com/tencentcloudapi/common/AbstractClient.java +++ b/src/main/java/com/tencentcloudapi/common/AbstractClient.java @@ -123,7 +123,12 @@ public AbstractClient( this.endpoint = endpoint; this.service = endpoint.split("\\.")[0]; this.region = region; - this.path = "/"; + int pathIdx = endpoint.indexOf('/'); + if (pathIdx >= 0) { + this.path = endpoint.substring(pathIdx); + } else { + this.path = "/"; + } this.sdkVersion = AbstractClient.SDK_VERSION; this.apiVersion = version; this.gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create(); @@ -212,7 +217,7 @@ public String call(String action, String jsonPayload) throws TencentCloudSDKExce byte[] requestPayload = jsonPayload.getBytes(StandardCharsets.UTF_8); String authorization = this.getAuthorization(headers, requestPayload); headers.put("Authorization", authorization); - String url = this.profile.getHttpProfile().getProtocol() + this.getEndpoint() + this.path; + String url = this.profile.getHttpProfile().getProtocol() + this.getEndpoint(); return this.getResponseBody(url, headers, requestPayload); } @@ -233,7 +238,7 @@ public String callOctetStream(String action, HashMap headers, by headers.put("Content-Type", "application/octet-stream; charset=utf-8"); String authorization = this.getAuthorization(headers, body); headers.put("Authorization", authorization); - String url = this.profile.getHttpProfile().getProtocol() + this.getEndpoint() + this.path; + String url = this.profile.getHttpProfile().getProtocol() + this.getEndpoint(); return this.getResponseBody(url, headers, body); } @@ -249,7 +254,7 @@ private HashMap getHeaders() { headers.put("X-TC-Version", this.apiVersion); headers.put("X-TC-Region", this.getRegion()); headers.put("X-TC-RequestClient", SDK_VERSION); - headers.put("Host", this.getEndpoint()); + headers.put("Host", this.getHost()); String token = this.credential.getToken(); if (token != null && !token.isEmpty()) { headers.put("X-TC-Token", token); @@ -273,7 +278,7 @@ private HashMap getHeaders() { */ private String getAuthorization(HashMap headers, byte[] body) throws TencentCloudSDKException { - String endpoint = this.getEndpoint(); + String host = this.getHost(); // always use post tc3-hmac-sha256 signature process // okhttp always set charset even we don't specify it, // to ensure signature be correct, we have to set it here as well. @@ -281,7 +286,7 @@ private String getAuthorization(HashMap headers, byte[] body) byte[] requestPayload = body; String canonicalUri = "/"; String canonicalQueryString = ""; - String canonicalHeaders = "content-type:" + contentType + "\nhost:" + endpoint + "\n"; + String canonicalHeaders = "content-type:" + contentType + "\nhost:" + host + "\n"; String signedHeaders = "content-type;host"; String hashedRequestPayload = ""; @@ -307,7 +312,7 @@ private String getAuthorization(HashMap headers, byte[] body) SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); String date = sdf.format(new Date(Long.valueOf(timestamp + "000"))); - String service = endpoint.split("\\.")[0]; + String service = host.split("\\.")[0]; String credentialScope = date + "/" + service + "/" + "tc3_request"; String hashedCanonicalRequest = Sign.sha256Hex(canonicalRequest.getBytes(StandardCharsets.UTF_8)); @@ -713,18 +718,27 @@ private Response doRequest(String endpoint, AbstractModel request, String action throws TencentCloudSDKException, IOException { HashMap param = new HashMap(); request.toMap(param, ""); - String strParam = this.formatRequestData(action, param); + String strParam = this.formatRequestData(action, request, param); String reqMethod = this.profile.getHttpProfile().getReqMethod(); String protocol = this.profile.getHttpProfile().getProtocol(); - String url = protocol + endpoint + this.path; + String url = protocol + endpoint; String apigwEndpoint = this.profile.getHttpProfile().getApigwEndpoint(); if (null != apigwEndpoint) { url = protocol + apigwEndpoint; } + + Builder headers = new Headers.Builder(); + if (null != request.GetHeader()) { + for (Map.Entry entry : request.GetHeader().entrySet()) { + headers.add(entry.getKey(), entry.getValue()); + } + } if (reqMethod.equals(HttpProfile.REQ_GET)) { - return this.httpConnection.getRequest(url + "?" + strParam); + return this.httpConnection.getRequest(url + "?" + strParam, headers.build()); } else if (reqMethod.equals(HttpProfile.REQ_POST)) { - return this.httpConnection.postRequest(url, strParam); + headers.add("X-TC-RequestClient", SDK_VERSION); + headers.add("Content-Type", "application/x-www-form-urlencoded"); + return this.httpConnection.postRequest(url, strParam, headers.build()); } else { throw new TencentCloudSDKException("Method only support (GET, POST)"); } @@ -770,9 +784,14 @@ private Response doRequestWithTC3(String endpoint, AbstractModel request, String contentType = "application/json; charset=utf-8"; } // Construct the canonical request for signature calculation. + String host = this.getHost(); + if (request.GetHeader().containsKey("Host")) { + host = request.GetHeader().get("Host"); + request.GetHeader().remove("Host"); + } String canonicalUri = "/"; String canonicalQueryString = this.getCanonicalQueryString(params, httpRequestMethod); - String canonicalHeaders = "content-type:" + contentType + "\nhost:" + endpoint + "\n"; + String canonicalHeaders = "content-type:" + contentType + "\nhost:" + host + "\n"; String signedHeaders = "content-type;host"; String hashedRequestPayload = ""; @@ -798,7 +817,7 @@ private Response doRequestWithTC3(String endpoint, AbstractModel request, String SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); sdf.setTimeZone(TimeZone.getTimeZone("UTC")); String date = sdf.format(new Date(Long.valueOf(timestamp + "000"))); - String service = endpoint.split("\\.")[0]; + String service = host.split("\\.")[0]; String credentialScope = date + "/" + service + "/" + "tc3_request"; String hashedCanonicalRequest = Sign.sha256Hex(canonicalRequest.getBytes(StandardCharsets.UTF_8)); @@ -831,7 +850,7 @@ private Response doRequestWithTC3(String endpoint, AbstractModel request, String } Builder hb = new Headers.Builder(); hb.add("Content-Type", contentType) - .add("Host", endpoint) + .add("Host", host) .add("Authorization", authorization) .add("X-TC-Action", action) .add("X-TC-Timestamp", timestamp) @@ -857,7 +876,7 @@ private Response doRequestWithTC3(String endpoint, AbstractModel request, String } String protocol = this.profile.getHttpProfile().getProtocol(); - String url = protocol + endpoint + this.path; + String url = protocol + endpoint; String apigwEndpoint = this.profile.getHttpProfile().getApigwEndpoint(); if (null != apigwEndpoint) { url = protocol + apigwEndpoint; @@ -954,7 +973,7 @@ private String getCanonicalQueryString(HashMap params, String me * @return The formatted string for signing. * @throws TencentCloudSDKException If UTF-8 encoding is not supported. */ - private String formatRequestData(String action, Map param) + private String formatRequestData(String action, AbstractModel request, Map param) throws TencentCloudSDKException { param.put("Action", action); param.put("RequestClient", this.sdkVersion); @@ -983,14 +1002,17 @@ private String formatRequestData(String action, Map param) param.put("Language", this.profile.getLanguage().getValue()); } - String endpoint = this.getEndpoint(); + String host = this.getHost(); + if (request.GetHeader().containsKey("Host")) { + host = request.GetHeader().get("Host"); + } // Generate the string to be signed. String sigInParam = Sign.makeSignPlainText( new TreeMap(param), this.profile.getHttpProfile().getReqMethod(), - endpoint, + host, this.path); // Generate the signature. String sigOutParam = @@ -1048,6 +1070,15 @@ private String getEndpoint() { } } + private String getHost() { + String endpoint = getEndpoint(); + int pathIdx = endpoint.indexOf('/'); + if (pathIdx < 0) { + pathIdx = endpoint.length(); + } + return endpoint.substring(0, pathIdx); + } + /** * 请注意购买类接口谨慎调用,可能导致多次购买 * 仅幂等接口推荐使用 diff --git a/src/main/java/com/tencentcloudapi/common/http/HttpConnection.java b/src/main/java/com/tencentcloudapi/common/http/HttpConnection.java index f837274e79..0262cd87cf 100644 --- a/src/main/java/com/tencentcloudapi/common/http/HttpConnection.java +++ b/src/main/java/com/tencentcloudapi/common/http/HttpConnection.java @@ -73,17 +73,6 @@ public Response doRequest(Request request) throws IOException { return this.client.newCall(request).execute(); } - public Response getRequest(String url) throws TencentCloudSDKException, IOException { - Request request = null; - try { - request = new Request.Builder().url(url).get().build(); - } catch (IllegalArgumentException e) { - throw new TencentCloudSDKException(e.getClass().getName() + "-" + e.getMessage()); - } - - return this.doRequest(request); - } - public Response getRequest(String url, Headers headers) throws TencentCloudSDKException, IOException { Request request = null; try { @@ -95,18 +84,6 @@ public Response getRequest(String url, Headers headers) throws TencentCloudSDKEx return this.doRequest(request); } - public Response postRequest(String url, String body) throws TencentCloudSDKException, IOException { - MediaType contentType = MediaType.parse("application/x-www-form-urlencoded"); - Request request = null; - try { - request = new Request.Builder().url(url).post(RequestBody.create(contentType, body)).build(); - } catch (IllegalArgumentException e) { - throw new TencentCloudSDKException(e.getClass().getName() + "-" + e.getMessage()); - } - - return this.doRequest(request); - } - public Response postRequest(String url, String body, Headers headers) throws TencentCloudSDKException, IOException { MediaType contentType = MediaType.parse(headers.get("Content-Type")); diff --git a/src/test/java/com/tencentcloudapi/integration/requests/HeaderTest.java b/src/test/java/com/tencentcloudapi/integration/requests/HeaderTest.java new file mode 100644 index 0000000000..f3f80ea5de --- /dev/null +++ b/src/test/java/com/tencentcloudapi/integration/requests/HeaderTest.java @@ -0,0 +1,50 @@ +package com.tencentcloudapi.integration.requests; + +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.exception.TencentCloudSDKException; +import com.tencentcloudapi.common.profile.ClientProfile; +import com.tencentcloudapi.common.profile.HttpProfile; +import com.tencentcloudapi.cvm.v20170312.CvmClient; +import com.tencentcloudapi.cvm.v20170312.models.DescribeInstancesRequest; +import org.junit.Test; + +import java.util.HashMap; + +public class HeaderTest { + @Test + public void TestSetHeader() throws TencentCloudSDKException { + Credential cred = new Credential( + System.getenv("TENCENTCLOUD_SECRET_ID"), + System.getenv("TENCENTCLOUD_SECRET_KEY") + ); + + String[] signMethods = new String[]{ + ClientProfile.SIGN_SHA1, + ClientProfile.SIGN_SHA256, + ClientProfile.SIGN_TC3_256 + }; + String[] reqMethods = new String[]{ + HttpProfile.REQ_POST, + HttpProfile.REQ_GET, + }; + + for (String signMethod : signMethods) { + for (String reqMethod : reqMethods) { + ClientProfile cpf = new ClientProfile(); + cpf.setSignMethod(signMethod); + HttpProfile hpf = new HttpProfile(); + hpf.setReqMethod(reqMethod); + hpf.setProtocol("http://"); + hpf.setEndpoint("9.134.89.153:81/tc_api"); + cpf.setHttpProfile(hpf); + + CvmClient client = new CvmClient(cred, "ap-guangzhou", cpf); + DescribeInstancesRequest req = new DescribeInstancesRequest(); + req.SetHeader(new HashMap() {{ + put("Host", "cvm.tencentcloudapi.com"); + }}); + client.DescribeInstances(req); + } + } + } +} diff --git a/src/test/java/com/tencentcloudapi/integration/requests/SignMethodTest.java b/src/test/java/com/tencentcloudapi/integration/requests/SignMethodTest.java index 0ccc119633..f8ac195dcc 100644 --- a/src/test/java/com/tencentcloudapi/integration/requests/SignMethodTest.java +++ b/src/test/java/com/tencentcloudapi/integration/requests/SignMethodTest.java @@ -67,7 +67,7 @@ public void TestCommonClient() throws TencentCloudSDKException { hpf.setReqMethod(reqMethod); cpf.setHttpProfile(hpf); - CommonClient client = new CommonClient("cvm", "2017-03-12", cred, "ap-guangzhou"); + CommonClient client = new CommonClient("cvm", "2017-03-12", cred, "ap-guangzhou", cpf); DescribeInstancesRequest req = new DescribeInstancesRequest(); client.commonRequest(req, "DescribeInstances"); } @@ -99,7 +99,7 @@ public void TestCommonClientCall() throws TencentCloudSDKException { hpf.setReqMethod(reqMethod); cpf.setHttpProfile(hpf); - CommonClient client = new CommonClient("cvm", "2017-03-12", cred, "ap-guangzhou"); + CommonClient client = new CommonClient("cvm", "2017-03-12", cred, "ap-guangzhou", cpf); client.call("DescribeInstances", "{\"Filters\":" + "[{\"Name\":\"zone\","