github.com/oam-dev/kubevela@v1.9.11/pkg/builtin/http/http.go (about)

     1  /*
     2  Copyright 2021 The KubeVela Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package http
    18  
    19  import (
    20  	"context"
    21  	"crypto/tls"
    22  	"crypto/x509"
    23  	"io"
    24  	"net/http"
    25  	"time"
    26  
    27  	"cuelang.org/go/cue"
    28  	"github.com/pkg/errors"
    29  
    30  	"github.com/kubevela/workflow/pkg/cue/model/value"
    31  
    32  	"github.com/oam-dev/kubevela/pkg/builtin/registry"
    33  )
    34  
    35  func init() {
    36  	registry.RegisterRunner("http", newHTTPCmd)
    37  }
    38  
    39  // HTTPCmd provides methods for http task
    40  type HTTPCmd struct{}
    41  
    42  func newHTTPCmd(_ cue.Value) (registry.Runner, error) {
    43  	return &HTTPCmd{}, nil
    44  }
    45  
    46  // Run exec the actual http logic, and res represent the result of http task
    47  func (c *HTTPCmd) Run(meta *registry.Meta) (res interface{}, err error) {
    48  	var header, trailer http.Header
    49  	var (
    50  		method = meta.String("method")
    51  		u      = meta.String("url")
    52  	)
    53  	var (
    54  		r      io.Reader
    55  		client = &http.Client{
    56  			Transport: http.DefaultTransport,
    57  			Timeout:   time.Second * 3,
    58  		}
    59  	)
    60  	if obj := meta.Obj.LookupPath(value.FieldPath("request")); obj.Exists() {
    61  		if v := obj.LookupPath(value.FieldPath("body")); v.Exists() {
    62  			r, err = v.Reader()
    63  			if err != nil {
    64  				return nil, err
    65  			}
    66  		}
    67  		if header, err = parseHeaders(obj, "header"); err != nil {
    68  			return nil, err
    69  		}
    70  		if trailer, err = parseHeaders(obj, "trailer"); err != nil {
    71  			return nil, err
    72  		}
    73  	}
    74  	if header == nil {
    75  		header = map[string][]string{}
    76  		header.Set("Content-Type", "application/json")
    77  	}
    78  	if meta.Err != nil {
    79  		return nil, meta.Err
    80  	}
    81  
    82  	req, err := http.NewRequestWithContext(context.Background(), method, u, r)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	req.Header = header
    87  	req.Trailer = trailer
    88  
    89  	if tlsConfig := meta.Obj.LookupPath(value.FieldPath("tls_config")); tlsConfig.Exists() {
    90  		tr := &http.Transport{
    91  			TLSClientConfig: &tls.Config{
    92  				NextProtos: []string{"http/1.1"},
    93  			},
    94  		}
    95  		ca := tlsConfig.LookupPath(value.FieldPath("ca"))
    96  		if caCrt, err := ca.String(); err != nil {
    97  			return nil, errors.WithMessage(err, "parse ca")
    98  		} else {
    99  			pool := x509.NewCertPool()
   100  			pool.AppendCertsFromPEM([]byte(caCrt))
   101  			tr.TLSClientConfig.RootCAs = pool
   102  		}
   103  
   104  		cert := tlsConfig.LookupPath(value.FieldPath("client_crt"))
   105  		key := tlsConfig.LookupPath(value.FieldPath("client_key"))
   106  		if cert.Exists() && key.Exists() {
   107  			crtData, err := cert.String()
   108  			if err != nil {
   109  				return nil, err
   110  			}
   111  			keyData, err := key.String()
   112  			if err != nil {
   113  				return nil, err
   114  			}
   115  			cliCrt, err := tls.X509KeyPair([]byte(crtData), []byte(keyData))
   116  			if err != nil {
   117  				return nil, errors.WithMessage(err, "parse client keypair")
   118  			}
   119  			tr.TLSClientConfig.Certificates = []tls.Certificate{cliCrt}
   120  		}
   121  
   122  		client.Transport = tr
   123  	}
   124  	resp, err := client.Do(req)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  	//nolint:errcheck
   129  	defer resp.Body.Close()
   130  	b, err := io.ReadAll(resp.Body)
   131  	// parse response body and headers
   132  	return map[string]interface{}{
   133  		"body":       string(b),
   134  		"header":     resp.Header,
   135  		"trailer":    resp.Trailer,
   136  		"statusCode": resp.StatusCode,
   137  	}, err
   138  }
   139  
   140  func parseHeaders(obj cue.Value, label string) (http.Header, error) {
   141  	m := obj.LookupPath(value.FieldPath(label))
   142  	if !m.Exists() {
   143  		return nil, nil
   144  	}
   145  	iter, err := m.Fields()
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  	h := http.Header{}
   150  	for iter.Next() {
   151  		str, err := iter.Value().String()
   152  		if err != nil {
   153  			return nil, err
   154  		}
   155  		h.Add(iter.Label(), str)
   156  	}
   157  	return h, nil
   158  }