Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions examples/cvm/v20170312/nginx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package main

import (
"fmt"
"os"

"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/regions"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)

func main() {
/*
本示例演示如何通过 **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;
}
}
*/
credential := common.NewCredential(
os.Getenv("TENCENTCLOUD_SECRET_ID"),
os.Getenv("TENCENTCLOUD_SECRET_KEY"),
)

cpf := profile.NewClientProfile()
// 2. 将 Scheme 设置为 http
cpf.HttpProfile.Scheme = "http"

// 3. 替换 1.2.3.4 为真实的 nginx 地址, /tc_api 可以自定义
nginx := "1.2.3.4/tc_api"
cpf.HttpProfile.Endpoint = nginx

client, err := cvm.NewClient(credential, regions.Guangzhou, cpf)
if err != nil {
panic(err)
}

request := cvm.NewDescribeInstancesRequest()
request.Limit = common.Int64Ptr(10)
// 4. 设置 header 为 {服务名}.tencentcloudapi.com
request.SetHeader(map[string]string{
"Host": "cvm.tencentcloudapi.com",
})

response, err := client.DescribeInstances(request)
if err != nil {
panic(err)
}
fmt.Println(response.ToJsonString())
}
55 changes: 29 additions & 26 deletions tencentcloud/common/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,14 @@ func (c *Client) completeRequest(request tchttp.Request) {
if domain == "" {
domain = request.GetServiceDomain(request.GetService())
}
request.SetDomain(domain)
pathIdx := strings.IndexByte(domain, '/')
if pathIdx >= 0 {
request.SetDomain(domain[:pathIdx])
request.SetPath(domain[pathIdx:])
} else {
request.SetDomain(domain)
request.SetPath("/")
}
}

if request.GetHttpMethod() == "" {
Expand Down Expand Up @@ -197,16 +204,6 @@ func (c *Client) sendWithoutSignature(request tchttp.Request, response tchttp.Re
}
}

for k, v := range request.GetHeader() {
switch k {
case "X-TC-Action", "X-TC-Version", "X-TC-Timestamp", "X-TC-RequestClient",
"X-TC-Language", "Content-Type", "X-TC-Region", "X-TC-Token":
c.logger.Printf("Skip header \"%s\": can not specify built-in header", k)
default:
headers[k] = v
}
}

if !isOctetStream && request.GetContentType() == octetStream {
isOctetStream = true
b, _ := json.Marshal(request)
Expand Down Expand Up @@ -269,7 +266,11 @@ func (c *Client) sendWithoutSignature(request tchttp.Request, response tchttp.Re
}
httpRequest = httpRequest.WithContext(request.GetContext())
for k, v := range headers {
httpRequest.Header[k] = []string{v}
if strings.EqualFold(k, "Host") {
httpRequest.Host = v
} else {
httpRequest.Header.Set(k, v)
}
}
httpResponse, err := c.sendWithRateLimitRetry(httpRequest, isRetryable(request))
if err != nil {
Expand Down Expand Up @@ -300,7 +301,11 @@ func (c *Client) sendWithSignatureV1(request tchttp.Request, response tchttp.Res
}

for k, v := range request.GetHeader() {
httpRequest.Header.Set(k, v)
if strings.EqualFold(k, "Host") {
httpRequest.Host = v
} else {
httpRequest.Header.Set(k, v)
}
}

httpResponse, err := c.sendWithRateLimitRetry(httpRequest, isRetryable(request))
Expand Down Expand Up @@ -358,18 +363,6 @@ func (c *Client) sendWithSignatureV3(request tchttp.Request, response tchttp.Res
}
}

// Merge any additional headers from the request, but skip built-in headers
// to prevent them from being overridden.
for k, v := range request.GetHeader() {
switch k {
case "X-TC-Action", "X-TC-Version", "X-TC-Timestamp", "X-TC-RequestClient",
"X-TC-Language", "X-TC-Region", "X-TC-Token":
c.logger.Printf("Skip header \"%s\": can not specify built-in header", k)
default:
headers[k] = v
}
}

// Handle the case where the request content type is explicitly set to octet-stream,
// but it's not already handled as an OctetStream CommonRequest.
if !isOctetStream && request.GetContentType() == octetStream {
Expand All @@ -385,6 +378,12 @@ func (c *Client) sendWithSignatureV3(request tchttp.Request, response tchttp.Res
headers["Content-Type"] = octetStream
octetStreamBody = request.GetBody()
}

// Merge any additional headers from the request
for k, v := range request.GetHeader() {
headers[k] = v
}

// --- Begin Signature Version 3 (TC3-HMAC-SHA256) Signing Process ---

// 1. Construct the Canonical Request
Expand Down Expand Up @@ -528,7 +527,11 @@ func (c *Client) sendWithSignatureV3(request tchttp.Request, response tchttp.Res

// Set all the headers on the request.
for k, v := range headers {
httpRequest.Header[k] = []string{v}
if strings.EqualFold(k, "Host") {
httpRequest.Host = v
} else {
httpRequest.Header.Set(k, v)
}
}

// Send the HTTP request with rate limit retry logic.
Expand Down
5 changes: 4 additions & 1 deletion tencentcloud/common/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ func signRequest(request tchttp.Request, credential CredentialIface, method stri

func getStringToSign(request tchttp.Request) string {
method := request.GetHttpMethod()
domain := request.GetDomain()
domain := request.GetHeader()["Host"]
if domain == "" {
domain = request.GetDomain()
}
path := request.GetPath()

var buf bytes.Buffer
Expand Down
91 changes: 91 additions & 0 deletions testing/integration/header_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Copyright 1999-2021 Tencent Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package integration

import (
"context"
"net/http/httptrace"
"os"
"strings"
"testing"

"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
)

func TestSetHeader(t *testing.T) {
signMethods := []string{"TC3-HMAC-SHA256", "HmacSHA256", "HmacSHA1"}
reqMethods := []string{"GET", "POST"}

endpoint := "vpc.tencentcloudapi.com"
signHost := "cvm.tencentcloudapi.com"

for _, signMethod := range signMethods {
for _, reqMethod := range reqMethods {
credential := common.NewCredential(
os.Getenv("TENCENTCLOUD_SECRET_ID"),
os.Getenv("TENCENTCLOUD_SECRET_KEY"),
)
cpf := profile.NewClientProfile()
cpf.HttpProfile.Endpoint = endpoint
cpf.HttpProfile.ReqMethod = reqMethod
cpf.SignMethod = signMethod
client, err := cvm.NewClient(credential, "ap-guangzhou", cpf)
if err != nil {
t.Fatal(err)
}

request := cvm.NewDescribeZonesRequest()
request.SetHeader(map[string]string{
"TestKey": "TestValue",
"Host": signHost,
})

var hostChecked, testKeyChecked bool
trace := &httptrace.ClientTrace{
DNSStart: func(info httptrace.DNSStartInfo) {
if info.Host != endpoint {
t.Fatalf("invalid req endpoint: signMethod=%s reqMethod=%s host=%s", signMethod, reqMethod, info.Host)
}
},
WroteHeaderField: func(key string, value []string) {
if strings.EqualFold(key, "Host") {
hostChecked = true
if value[0] != signHost {
t.Fatalf("invalid Host: %s", value[0])
}
}

if strings.EqualFold(key, "TestKey") {
testKeyChecked = true
if value[0] != "TestValue" {
t.Fatalf("invalid Header: %s", value[0])
}
}
},
}
ctx := httptrace.WithClientTrace(context.Background(), trace)
_, err = client.DescribeZonesWithContext(ctx, request)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}

if !hostChecked || !testKeyChecked {
t.Fatalf("header not sent: %v %v", hostChecked, testKeyChecked)
}
}
}
}