JdcloudClient.go raw

   1  // Copyright 2018-2025 JDCLOUD.COM
   2  //
   3  // Licensed under the Apache License, Version 2.0 (the "License");
   4  // you may not use this file except in compliance with the License.
   5  // You may obtain a copy of the License at
   6  //
   7  //     http://www.apache.org/licenses/LICENSE-2.0
   8  //
   9  // Unless required by applicable law or agreed to in writing, software
  10  // distributed under the License is distributed on an "AS IS" BASIS,
  11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12  // See the License for the specific language governing permissions and
  13  // limitations under the License.
  14  
  15  package core
  16  
  17  import (
  18  	"encoding/base64"
  19  	"encoding/json"
  20  	"fmt"
  21  	"net/http"
  22  	"strings"
  23  	"time"
  24  )
  25  
  26  // JDCloudClient is the base struct of service clients
  27  type JDCloudClient struct {
  28  	Credential  Credential
  29  	Config      Config
  30  	ServiceName string
  31  	Revision    string
  32  	Logger      Logger
  33  }
  34  
  35  type SignFunc func(*http.Request) error
  36  
  37  // Send send the request and return the response to the client.
  38  // Parameter request accepts concrete request object which follow RequestInterface.
  39  func (c JDCloudClient) Send(request RequestInterface, serviceName string) ([]byte, error) {
  40  	method := request.GetMethod()
  41  	builder := GetParameterBuilder(method, c.Logger)
  42  	jsonReq, _ := json.Marshal(request)
  43  	encodedUrl, err := builder.BuildURL(request.GetURL(), jsonReq)
  44  	if err != nil {
  45  		return nil, err
  46  	}
  47  	reqUrl := fmt.Sprintf("%s://%s/%s%s", c.Config.Scheme, c.Config.Endpoint, request.GetVersion(), encodedUrl)
  48  
  49  	body, err := builder.BuildBody(jsonReq)
  50  	if err != nil {
  51  		return nil, err
  52  	}
  53  
  54  	sign := func(r *http.Request) error {
  55  		regionId := request.GetRegionId()
  56  		// some request has no region parameter, so give a value to it,
  57  		// then API gateway can calculate sign successfully.
  58  		if regionId == "" {
  59  			regionId = "jdcloud-api"
  60  		}
  61  
  62  		signer := NewSigner(c.Credential, c.Logger)
  63  		_, err := signer.Sign(r, strings.NewReader(body), serviceName, regionId, time.Now())
  64  		return err
  65  	}
  66  
  67  	return c.doSend(method, reqUrl, body, request.GetHeaders(), c.Config.Timeout, sign)
  68  }
  69  
  70  func (c JDCloudClient) doSend(method, url, data string, header map[string]string, timeout time.Duration, sign SignFunc) ([]byte, error) {
  71  	client := &http.Client{Timeout: timeout}
  72  
  73  	req, err := http.NewRequest(method, url, strings.NewReader(data))
  74  	if err != nil {
  75  		c.Logger.Log(LogFatal, err.Error())
  76  		return nil, err
  77  	}
  78  
  79  	c.setHeader(req, header)
  80  
  81  	err = sign(req)
  82  	if err != nil {
  83  		return nil, err
  84  	}
  85  
  86  	resp, err := client.Do(req)
  87  	if err != nil {
  88  		c.Logger.Log(LogError, err.Error())
  89  		return nil, err
  90  	}
  91  
  92  	processor := GetResponseProcessor(req.Method)
  93  	result, err := processor.Process(resp)
  94  	if err != nil {
  95  		c.Logger.Log(LogError, err.Error())
  96  		return nil, err
  97  	}
  98  	return result, nil
  99  }
 100  
 101  func (c JDCloudClient) setHeader(req *http.Request, header map[string]string) {
 102  
 103  	req.Header.Set("Content-Type", "application/json")
 104  	req.Header.Set("User-Agent", fmt.Sprintf("JdcloudSdkGo/%s %s/%s", Version, c.ServiceName, c.Revision))
 105  
 106  	base64Headers := []string{HeaderJdcloudPrefix + "-pin", HeaderJdcloudPrefix + "-erp", HeaderJdcloudPrefix + "-security-token",
 107  		HeaderJcloudPrefix + "-pin", HeaderJcloudPrefix + "-erp", HeaderJcloudPrefix + "-security-token"}
 108  
 109  	for k, v := range header {
 110  		if includes(base64Headers, strings.ToLower(k)) {
 111  			v = base64.StdEncoding.EncodeToString([]byte(v))
 112  		}
 113  
 114  		req.Header.Set(k, v)
 115  	}
 116  
 117  	for k, v := range req.Header {
 118  		c.Logger.Log(LogInfo, k, v)
 119  	}
 120  }
 121