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