Compare commits

...

1 Commits

Author SHA1 Message Date
nanhe 1233c07df9 feat: support custom httpClient 2025-03-26 17:59:12 +08:00
4 changed files with 132 additions and 29 deletions
+35
View File
@@ -0,0 +1,35 @@
{
"scope": "darabonba",
"name": "HttpClient",
"version": "0.0.1",
"main": "./main.dara",
"maintainers": [
{
"name": "Alibaba Cloud SDK",
"email": "sdk-team@alibabacloud.com"
}
],
"libraries": {
},
"releases": {
"go": "github.com/alibabacloud-go/tea/tea:v1.2.3-0.20240605082020-e6e537a31150"
},
"go": {
"interface": true,
"clientName": "HttpClient",
"typedef": {
"HttpRequest": {
"import": "net/http",
"type": "http.Request"
},
"HttpResponse": {
"import": "net/http",
"type": "http.Response"
},
"HttpTransport": {
"import": "net/http",
"type": "http.Transport"
}
}
}
}
+8
View File
@@ -0,0 +1,8 @@
typedef HttpRequest;
typedef HttpResponse;
typedef HttpTransport;
init(){
}
async function call(request: HttpRequest, transport: HttpTransport): HttpResponse;
+47 -19
View File
@@ -31,7 +31,28 @@ import (
var debugLog = debug.Init("tea") var debugLog = debug.Init("tea")
var hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { type HttpRequest interface {
}
type HttpResponse interface {
}
type HttpClient interface {
Call(request *http.Request, transport *http.Transport) (response *http.Response, err error)
}
type teaClient struct {
sync.Mutex
httpClient *http.Client
ifInit bool
}
func (client *teaClient) Call(request *http.Request, transport *http.Transport) (response *http.Response, err error) {
response, err = client.httpClient.Do(request)
return
}
var hookDo = func(fn func(req *http.Request, transport *http.Transport) (*http.Response, error)) func(req *http.Request, transport *http.Transport) (*http.Response, error) {
return fn return fn
} }
@@ -97,12 +118,7 @@ type RuntimeObject struct {
Listener utils.ProgressListener `json:"listener" xml:"listener"` Listener utils.ProgressListener `json:"listener" xml:"listener"`
Tracker *utils.ReaderTracker `json:"tracker" xml:"tracker"` Tracker *utils.ReaderTracker `json:"tracker" xml:"tracker"`
Logger *utils.Logger `json:"logger" xml:"logger"` Logger *utils.Logger `json:"logger" xml:"logger"`
} HttpClient
type teaClient struct {
sync.Mutex
httpClient *http.Client
ifInit bool
} }
var clientPool = &sync.Map{} var clientPool = &sync.Map{}
@@ -143,6 +159,9 @@ func NewRuntimeObject(runtime map[string]interface{}) *RuntimeObject {
if runtime["logger"] != nil { if runtime["logger"] != nil {
runtimeObject.Logger = runtime["logger"].(*utils.Logger) runtimeObject.Logger = runtime["logger"].(*utils.Logger)
} }
if runtime["httpClient"] != nil {
runtimeObject.HttpClient = runtime["httpClient"].(HttpClient)
}
return runtimeObject return runtimeObject
} }
@@ -351,18 +370,27 @@ func DoRequest(request *Request, requestRuntime map[string]interface{}) (respons
} }
httpRequest.Host = StringValue(request.Domain) httpRequest.Host = StringValue(request.Domain)
client := getTeaClient(runtimeObject.getClientTag(StringValue(request.Domain))) var client HttpClient
client.Lock() if runtimeObject.HttpClient == nil {
if !client.ifInit { client = getTeaClient(runtimeObject.getClientTag(StringValue(request.Domain)))
trans, err := getHttpTransport(request, runtimeObject) } else {
if err != nil { client = runtimeObject.HttpClient
return nil, err
}
client.httpClient.Timeout = time.Duration(IntValue(runtimeObject.ReadTimeout)) * time.Millisecond
client.httpClient.Transport = trans
client.ifInit = true
} }
client.Unlock()
trans, err := getHttpTransport(request, runtimeObject)
if err != nil {
return
}
if defaultClient, ok := client.(*teaClient); ok {
defaultClient.Lock()
if !defaultClient.ifInit || defaultClient.httpClient.Transport == nil {
defaultClient.httpClient.Transport = trans
}
defaultClient.httpClient.Timeout = time.Duration(IntValue(runtimeObject.ReadTimeout)) * time.Millisecond
defaultClient.ifInit = true
defaultClient.Unlock()
}
for key, value := range request.Headers { for key, value := range request.Headers {
if value == nil || key == "content-length" { if value == nil || key == "content-length" {
continue continue
@@ -384,7 +412,7 @@ func DoRequest(request *Request, requestRuntime map[string]interface{}) (respons
putMsgToMap(fieldMap, httpRequest) putMsgToMap(fieldMap, httpRequest)
startTime := time.Now() startTime := time.Now()
fieldMap["{start_time}"] = startTime.Format("2006-01-02 15:04:05") fieldMap["{start_time}"] = startTime.Format("2006-01-02 15:04:05")
res, err := hookDo(client.httpClient.Do)(httpRequest) res, err := hookDo(client.Call)(httpRequest, trans)
fieldMap["{cost}"] = time.Since(startTime).String() fieldMap["{cost}"] = time.Since(startTime).String()
completedBytes := int64(0) completedBytes := int64(0)
if runtimeObject.Tracker != nil { if runtimeObject.Tracker != nil {
+42 -10
View File
@@ -487,11 +487,36 @@ func Test_GetBackoffTime(t *testing.T) {
utils.AssertEqual(t, true, IntValue(ms) <= 3) utils.AssertEqual(t, true, IntValue(ms) <= 3)
} }
type httpClient struct {
HttpClient
httpClient *http.Client
}
func newHttpClient() (*httpClient, error) {
client := new(httpClient)
err := client.Init()
return client, err
}
func (client *httpClient) Init() (_err error) {
return nil
}
func (client *httpClient) Call(request *http.Request, transport *http.Transport) (_result *http.Response, _err error) {
if transport != nil {
trans := transport.Clone()
client.httpClient.Transport = trans
} else {
client.httpClient.Transport = http.DefaultTransport.(*http.Transport).Clone()
}
return client.httpClient.Do(request)
}
func Test_DoRequest(t *testing.T) { func Test_DoRequest(t *testing.T) {
origTestHookDo := hookDo origTestHookDo := hookDo
defer func() { hookDo = origTestHookDo }() defer func() { hookDo = origTestHookDo }()
hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { hookDo = func(fn func(req *http.Request, transport *http.Transport) (*http.Response, error)) func(req *http.Request, transport *http.Transport) (*http.Response, error) {
return func(req *http.Request) (*http.Response, error) { return func(req *http.Request, transport *http.Transport) (*http.Response, error) {
return mockResponse(200, ``, errors.New("Internal error")) return mockResponse(200, ``, errors.New("Internal error"))
} }
} }
@@ -530,8 +555,8 @@ func Test_DoRequest(t *testing.T) {
utils.AssertNil(t, resp) utils.AssertNil(t, resp)
utils.AssertContains(t, err.Error(), ` invalid URL escape "%gf"`) utils.AssertContains(t, err.Error(), ` invalid URL escape "%gf"`)
hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { hookDo = func(fn func(req *http.Request, transport *http.Transport) (*http.Response, error)) func(req *http.Request, transport *http.Transport) (*http.Response, error) {
return func(req *http.Request) (*http.Response, error) { return func(req *http.Request, transport *http.Transport) (*http.Response, error) {
return mockResponse(200, ``, nil) return mockResponse(200, ``, nil)
} }
} }
@@ -579,8 +604,8 @@ func Test_DoRequest(t *testing.T) {
utils.AssertNil(t, err) utils.AssertNil(t, err)
utils.AssertEqual(t, "test", StringValue(resp.Headers["tea"])) utils.AssertEqual(t, "test", StringValue(resp.Headers["tea"]))
hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { hookDo = func(fn func(req *http.Request, transport *http.Transport) (*http.Response, error)) func(req *http.Request, transport *http.Transport) (*http.Response, error) {
return func(req *http.Request) (*http.Response, error) { return func(req *http.Request, transport *http.Transport) (*http.Response, error) {
utils.AssertEqual(t, "tea-cn-hangzhou.aliyuncs.com:1080", req.Host) utils.AssertEqual(t, "tea-cn-hangzhou.aliyuncs.com:1080", req.Host)
return mockResponse(200, ``, errors.New("Internal error")) return mockResponse(200, ``, errors.New("Internal error"))
} }
@@ -592,13 +617,20 @@ func Test_DoRequest(t *testing.T) {
resp, err = DoRequest(request, runtimeObj) resp, err = DoRequest(request, runtimeObj)
utils.AssertNil(t, resp) utils.AssertNil(t, resp)
utils.AssertEqual(t, `Internal error`, err.Error()) utils.AssertEqual(t, `Internal error`, err.Error())
httpClient, err := newHttpClient()
utils.AssertNil(t, err)
runtimeObj["httpClient"] = httpClient
resp, err = DoRequest(request, runtimeObj)
utils.AssertNil(t, resp)
utils.AssertEqual(t, `Internal error`, err.Error())
} }
func Test_DoRequestWithConcurrent(t *testing.T) { func Test_DoRequestWithConcurrent(t *testing.T) {
origTestHookDo := hookDo origTestHookDo := hookDo
defer func() { hookDo = origTestHookDo }() defer func() { hookDo = origTestHookDo }()
hookDo = func(fn func(req *http.Request) (*http.Response, error)) func(req *http.Request) (*http.Response, error) { hookDo = func(fn func(req *http.Request, transport *http.Transport) (*http.Response, error)) func(req *http.Request, transport *http.Transport) (*http.Response, error) {
return func(req *http.Request) (*http.Response, error) { return func(req *http.Request, transport *http.Transport) (*http.Response, error) {
return mockResponse(200, ``, nil) return mockResponse(200, ``, nil)
} }
} }
@@ -694,11 +726,11 @@ func Test_SetDialContext(t *testing.T) {
} }
func Test_hookdo(t *testing.T) { func Test_hookdo(t *testing.T) {
fn := func(req *http.Request) (*http.Response, error) { fn := func(req *http.Request, transport *http.Transport) (*http.Response, error) {
return nil, errors.New("hookdo") return nil, errors.New("hookdo")
} }
result := hookDo(fn) result := hookDo(fn)
resp, err := result(nil) resp, err := result(nil, nil)
utils.AssertNil(t, resp) utils.AssertNil(t, resp)
utils.AssertEqual(t, "hookdo", err.Error()) utils.AssertEqual(t, "hookdo", err.Error())
} }