1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package http
16
17 import (
18 "bytes"
19 "crypto/tls"
20 "crypto/x509"
21 "encoding/pem"
22 "io"
23 "net/http"
24
25 "cuelang.org/go/cue"
26 "cuelang.org/go/cue/errors"
27 "cuelang.org/go/internal/task"
28 )
29
30 func init() {
31 task.Register("tool/http.Do", newHTTPCmd)
32
33
34 task.Register("http", newHTTPCmd)
35 }
36
37 type httpCmd struct{}
38
39 func newHTTPCmd(v cue.Value) (task.Runner, error) {
40 return &httpCmd{}, nil
41 }
42
43 func (c *httpCmd) Run(ctx *task.Context) (res interface{}, err error) {
44 var header, trailer http.Header
45 var (
46 method = ctx.String("method")
47 u = ctx.String("url")
48 )
49 var r io.Reader
50 if obj := ctx.Obj.Lookup("request"); obj.Exists() {
51 if v := obj.Lookup("body"); v.Exists() {
52 r, err = v.Reader()
53 if err != nil {
54 return nil, err
55 }
56 } else {
57 r = bytes.NewReader([]byte(""))
58 }
59 if header, err = parseHeaders(obj, "header"); err != nil {
60 return nil, err
61 }
62 if trailer, err = parseHeaders(obj, "trailer"); err != nil {
63 return nil, err
64 }
65 }
66
67 var caCert []byte
68 caCertValue := ctx.Obj.LookupPath(cue.ParsePath("tls.caCert"))
69 if caCertValue.Exists() {
70 caCert, err = caCertValue.Bytes()
71 if err != nil {
72 return nil, errors.Wrapf(err, caCertValue.Pos(), "invalid bytes value")
73 }
74 }
75
76 tlsVerify := true
77 tlsVerifyValue := ctx.Obj.LookupPath(cue.ParsePath("tls.verify"))
78 if tlsVerifyValue.Exists() {
79 tlsVerify, err = tlsVerifyValue.Bool()
80 if err != nil {
81 return nil, errors.Wrapf(err, tlsVerifyValue.Pos(), "invalid bool value")
82 }
83 }
84
85 if ctx.Err != nil {
86 return nil, ctx.Err
87 }
88
89 transport := http.DefaultTransport.(*http.Transport).Clone()
90 transport.TLSClientConfig = &tls.Config{}
91
92 if !tlsVerify {
93 transport.TLSClientConfig.InsecureSkipVerify = true
94 }
95 if tlsVerify && len(caCert) > 0 {
96 pool := x509.NewCertPool()
97 for {
98 block, rest := pem.Decode(caCert)
99 if block == nil {
100 break
101 }
102 if block.Type == "PUBLIC KEY" {
103 c, err := x509.ParseCertificate(block.Bytes)
104 if err != nil {
105 return nil, errors.Wrapf(err, ctx.Obj.Pos(), "failed to parse caCert")
106 }
107 pool.AddCert(c)
108 }
109 caCert = rest
110 }
111 transport.TLSClientConfig.RootCAs = pool
112 }
113
114 client := &http.Client{
115 Transport: transport,
116
117 }
118
119 req, err := http.NewRequest(method, u, r)
120 if err != nil {
121 return nil, err
122 }
123 req.Header = header
124 req.Trailer = trailer
125
126
127 resp, err := client.Do(req)
128 if err != nil {
129 return nil, err
130 }
131 defer resp.Body.Close()
132 b, err := io.ReadAll(resp.Body)
133
134 return map[string]interface{}{
135 "response": map[string]interface{}{
136 "status": resp.Status,
137 "statusCode": resp.StatusCode,
138 "body": string(b),
139 "header": resp.Header,
140 "trailer": resp.Trailer,
141 },
142 }, err
143 }
144
145 func parseHeaders(obj cue.Value, label string) (http.Header, error) {
146 m := obj.Lookup(label)
147 if !m.Exists() {
148 return nil, nil
149 }
150 iter, err := m.Fields()
151 if err != nil {
152 return nil, err
153 }
154 h := http.Header{}
155 for iter.Next() {
156 str, err := iter.Value().String()
157 if err != nil {
158 return nil, err
159 }
160 h.Add(iter.Label(), str)
161 }
162 return h, nil
163 }
164
View as plain text