caller.go raw

   1  package dns
   2  
   3  import (
   4  	"bytes"
   5  	"context"
   6  	"encoding/json"
   7  	"fmt"
   8  	"io/ioutil"
   9  	"net/http"
  10  	"os"
  11  	"strconv"
  12  
  13  	"github.com/pkg/errors"
  14  	"github.com/volcengine/volc-sdk-golang/base"
  15  )
  16  
  17  func NewDefaultServiceInfo() *base.Client {
  18  	return base.NewClient(ServiceInfo, nil)
  19  }
  20  
  21  type VolcCaller struct {
  22  	Volc *base.Client
  23  }
  24  
  25  func NewVolcCaller() *VolcCaller {
  26  	instance := &VolcCaller{Volc: NewDefaultServiceInfo()}
  27  	instance.Volc.SetAccessKey(os.Getenv("VOLC_ACCESSKEY"))
  28  	instance.Volc.SetSecretKey(os.Getenv("VOLC_SECRETKEY"))
  29  	instance.Volc.SetHost(instance.Volc.ServiceInfo.Host)
  30  	instance.Volc.SetScheme(instance.Volc.ServiceInfo.Scheme)
  31  	instance.Volc.SetTimeout(instance.Volc.ServiceInfo.Timeout)
  32  
  33  	return instance
  34  }
  35  
  36  func (c *VolcCaller) Do(r *http.Request) (*http.Response, error) {
  37  	r.URL.Host = c.Volc.ServiceInfo.Host
  38  	r.URL.Scheme = c.Volc.ServiceInfo.Scheme
  39  	r.Host = c.Volc.ServiceInfo.Host
  40  
  41  	r.Header.Add("Content-Type", "application/json")
  42  	q := r.URL.Query()
  43  	q.Add("Version", ServiceVersion)
  44  	q.Add("X-Account-Id", xTopAccountID)
  45  	r.URL.RawQuery = q.Encode()
  46  
  47  	r = c.Volc.ServiceInfo.Credentials.Sign(r)
  48  
  49  	ctx, cancel := context.WithTimeout(r.Context(), c.Volc.ServiceInfo.Timeout)
  50  	defer cancel()
  51  	r = r.WithContext(ctx)
  52  
  53  	resp, err := c.Volc.Client.Do(r)
  54  	if err != nil {
  55  		return resp, errors.WithStack(err)
  56  	}
  57  
  58  	var payload TopResponse
  59  	err = json.NewDecoder(resp.Body).Decode(&payload)
  60  	resp.Body.Close()
  61  
  62  	newResp := &http.Response{}
  63  
  64  	if err != nil {
  65  		return newResp, errors.WithStack(err)
  66  	}
  67  
  68  	if payload.ResponseMetadata.Error.Code != "" {
  69  		return newResp, NewTOPError(&payload.ResponseMetadata)
  70  	}
  71  
  72  	if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
  73  		return newResp, errors.Wrap(errors.New("http error"), strconv.Itoa(resp.StatusCode))
  74  	}
  75  
  76  	str, err := json.Marshal(payload.Result)
  77  	if err != nil {
  78  		return newResp, errors.WithStack(err)
  79  	}
  80  
  81  	newResp = &http.Response{Body: ioutil.NopCloser(bytes.NewBuffer(str))}
  82  
  83  	return newResp, errors.WithStack(err)
  84  }
  85  
  86  type TOPError struct {
  87  	Code      string `form:"Code" json:"Code"`
  88  	CodeN     int64  `form:"CodeN" json:"CodeN"`
  89  	Message   string `form:"Message" json:"Message"`
  90  	RequestID string `form:"RequestId" json:"RequestId"`
  91  }
  92  
  93  func NewTOPError(respMeta *TopRespMeta) *TOPError {
  94  	return &TOPError{
  95  		Code:      respMeta.Error.Code,
  96  		CodeN:     respMeta.Error.CodeN,
  97  		Message:   respMeta.Error.Message,
  98  		RequestID: respMeta.RequestID,
  99  	}
 100  }
 101  
 102  func (err *TOPError) GetCode() int64 {
 103  	return err.CodeN
 104  }
 105  
 106  func (err *TOPError) Error() string {
 107  	return fmt.Sprintf(
 108  		"TOP DNS response failed with code %d: %s, %s, requestID: %s",
 109  		err.CodeN, err.Code, err.Message, err.RequestID,
 110  	)
 111  }
 112  
 113  type TopResponse struct {
 114  	ResponseMetadata TopRespMeta     `form:"ResponseMetadata" json:"ResponseMetadata"`
 115  	Result           json.RawMessage `form:"Result" json:"Result"`
 116  }
 117  
 118  type TopRespMeta struct {
 119  	Action    string       `form:"Action" json:"Action"`
 120  	Error     TopRespError `form:"Error" json:"Error"`
 121  	Region    string       `form:"Region" json:"Region"`
 122  	RequestID string       `form:"RequestId" json:"RequestId"`
 123  	Service   string       `form:"Service" json:"Service"`
 124  	Version   string       `form:"Version" json:"Version"`
 125  }
 126  
 127  type TopRespError struct {
 128  	Code    string `form:"Code" json:"Code"`
 129  	CodeN   int64  `form:"CodeN" json:"CodeN"`
 130  	Message string `form:"Message" json:"Message"`
 131  }
 132