cuelang.org/go@v0.10.1/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 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 // For backwards compatibility. 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.LookupPath(cue.MakePath(cue.Str("request"))); obj.Exists() { 51 if v := obj.LookupPath(cue.MakePath(cue.Str("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.MakePath(cue.Str("tls"), cue.Str("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.MakePath(cue.Str("tls"), cue.Str("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 // TODO: timeout 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 // TODO: retry logic 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 // parse response body and headers 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.LookupPath(cue.MakePath(cue.Str(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 }