github.com/GuanceCloud/cliutils@v1.1.21/pipeline/ptinput/funcs/fn_http_request.go (about) 1 package funcs 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "io" 7 "net" 8 "net/http" 9 "strconv" 10 "strings" 11 "time" 12 13 "github.com/GuanceCloud/platypus/pkg/ast" 14 "github.com/GuanceCloud/platypus/pkg/engine/runtime" 15 "github.com/GuanceCloud/platypus/pkg/errchain" 16 ) 17 18 var defaultTransport http.RoundTripper = &http.Transport{ 19 DialContext: ((&net.Dialer{ 20 Timeout: 30 * time.Second, 21 KeepAlive: 30 * time.Second, 22 }).DialContext), 23 MaxIdleConns: 100, 24 IdleConnTimeout: 90 * time.Second, 25 TLSHandshakeTimeout: 10 * time.Second, 26 ExpectContinueTimeout: 1 * time.Second, 27 } 28 29 func HTTPRequestChecking(ctx *runtime.Task, funcExpr *ast.CallExpr) *errchain.PlError { 30 if err := normalizeFuncArgsDeprecated(funcExpr, []string{ 31 "method", "url", "headers", "body", 32 }, 2); err != nil { 33 return runtime.NewRunError(ctx, err.Error(), funcExpr.NamePos) 34 } 35 36 return nil 37 } 38 39 func HTTPRequest(ctx *runtime.Task, funcExpr *ast.CallExpr) *errchain.PlError { 40 // Acquire params 41 method, methodType, err := runtime.RunStmt(ctx, funcExpr.Param[0]) 42 if err != nil { 43 return err 44 } 45 if methodType != ast.String { 46 return runtime.NewRunError(ctx, "param data type expect string", 47 funcExpr.Param[0].StartPos()) 48 } 49 50 url, urlType, err := runtime.RunStmt(ctx, funcExpr.Param[1]) 51 if err != nil { 52 return err 53 } 54 if urlType != ast.String { 55 return runtime.NewRunError(ctx, "param data type expect string", 56 funcExpr.Param[1].StartPos()) 57 } 58 59 var headers any 60 if funcExpr.Param[2] != nil { 61 var headersType ast.DType 62 headers, headersType, err = runtime.RunStmt(ctx, funcExpr.Param[2]) 63 if err != nil { 64 return err 65 } 66 if headersType != ast.Map { 67 return runtime.NewRunError(ctx, "param data type expect map", 68 funcExpr.Param[2].StartPos()) 69 } 70 } 71 72 var reqBody io.Reader 73 if funcExpr.Param[3] != nil { 74 val, _, err := runtime.RunStmt(ctx, funcExpr.Param[3]) 75 if err != nil { 76 return err 77 } 78 reqBody = buildBody(val) 79 } 80 81 // Send HTTP request 82 client := &http.Client{ 83 Transport: defaultTransport, 84 Timeout: time.Duration(10) * time.Second, 85 } 86 87 req, errR := http.NewRequest(method.(string), url.(string), reqBody) 88 if errR != nil { 89 ctx.Regs.ReturnAppend(nil, ast.Nil) 90 return nil 91 } 92 if headers != nil { 93 for k, v := range headers.(map[string]any) { 94 if v, ok := v.(string); ok { 95 req.Header.Set(k, v) 96 } 97 } 98 } 99 100 resp, errR := client.Do(req) 101 if errR != nil { 102 ctx.Regs.ReturnAppend(nil, ast.Nil) 103 return nil 104 } 105 106 defer func() { _ = resp.Body.Close() }() 107 108 body, errR := io.ReadAll(resp.Body) 109 if errR != nil { 110 ctx.Regs.ReturnAppend(nil, ast.Nil) 111 return nil 112 } 113 114 respData := map[string]interface{}{ 115 "status_code": resp.StatusCode, 116 "body": string(body), 117 } 118 ctx.Regs.ReturnAppend(respData, ast.Map) 119 120 return nil 121 } 122 123 func buildBody(val any) io.Reader { 124 switch val := val.(type) { 125 case string: 126 return strings.NewReader(val) 127 case []any: 128 if val, err := json.Marshal(val); err == nil { 129 return bytes.NewReader(val) 130 } 131 case map[string]any: 132 if val, err := json.Marshal(val); err == nil { 133 return bytes.NewReader(val) 134 } 135 case float64: 136 return strings.NewReader(strconv.FormatFloat(val, 'f', -1, 64)) 137 case int64: 138 return strings.NewReader(strconv.FormatInt(val, 10)) 139 case bool: 140 return strings.NewReader(strconv.FormatBool(val)) 141 default: 142 } 143 return nil 144 }