github.com/MontFerret/ferret@v0.18.0/pkg/stdlib/io/net/http/request.go (about)

     1  package http
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"io"
     7  	h "net/http"
     8  
     9  	"github.com/MontFerret/ferret/pkg/runtime/core"
    10  	"github.com/MontFerret/ferret/pkg/runtime/values"
    11  	"github.com/MontFerret/ferret/pkg/runtime/values/types"
    12  )
    13  
    14  type Params struct {
    15  	Method  values.String
    16  	URL     values.String
    17  	Headers *values.Object
    18  	Body    values.Binary
    19  }
    20  
    21  // REQUEST makes a HTTP request.
    22  // @param {Object} params - Request parameters.
    23  // @param {String} params.method - HTTP method
    24  // @param {String} params.url - Target url
    25  // @param {Binary} params.body - Request data
    26  // @param {Object} [params.headers] - HTTP headers
    27  // @return {Binary} - Response in binary format
    28  func REQUEST(ctx context.Context, args ...core.Value) (core.Value, error) {
    29  	return execMethod(ctx, "", args)
    30  }
    31  
    32  func execMethod(ctx context.Context, method values.String, args []core.Value) (core.Value, error) {
    33  	if err := core.ValidateArgs(args, 1, 1); err != nil {
    34  		return values.None, err
    35  	}
    36  
    37  	arg := args[0]
    38  
    39  	if err := core.ValidateType(arg, types.Object); err != nil {
    40  		return values.None, err
    41  	}
    42  
    43  	p, err := newParamsFrom(arg.(*values.Object))
    44  
    45  	if err != nil {
    46  		return values.None, err
    47  	}
    48  
    49  	if method != "" {
    50  		p.Method = method
    51  	}
    52  
    53  	return makeRequest(ctx, p)
    54  }
    55  
    56  func makeRequest(ctx context.Context, params Params) (core.Value, error) {
    57  	client := h.Client{}
    58  	req, err := h.NewRequest(params.Method.String(), params.URL.String(), bytes.NewBuffer(params.Body))
    59  
    60  	if err != nil {
    61  		return values.None, err
    62  	}
    63  
    64  	req.Header = h.Header{}
    65  
    66  	if params.Headers != nil {
    67  		params.Headers.ForEach(func(value core.Value, key string) bool {
    68  			req.Header.Set(key, value.String())
    69  
    70  			return true
    71  		})
    72  	}
    73  
    74  	resp, err := client.Do(req.WithContext(ctx))
    75  
    76  	if err != nil {
    77  		return values.None, err
    78  	}
    79  
    80  	data, err := io.ReadAll(resp.Body)
    81  
    82  	if err != nil {
    83  		return values.None, err
    84  	}
    85  
    86  	defer resp.Body.Close()
    87  
    88  	return values.NewBinary(data), nil
    89  }
    90  
    91  func newParamsFrom(obj *values.Object) (Params, error) {
    92  	p := Params{}
    93  
    94  	method, exists := obj.Get("method")
    95  
    96  	if exists {
    97  		p.Method = values.ToString(method)
    98  	}
    99  
   100  	url, exists := obj.Get("url")
   101  
   102  	if !exists {
   103  		return Params{}, core.Error(core.ErrMissedArgument, ".url")
   104  	}
   105  
   106  	p.URL = values.NewString(url.String())
   107  
   108  	headers, exists := obj.Get("headers")
   109  
   110  	if exists {
   111  		if err := core.ValidateType(headers, types.Object); err != nil {
   112  			return Params{}, core.Error(err, ".headers")
   113  		}
   114  
   115  		p.Headers = headers.(*values.Object)
   116  	}
   117  
   118  	body, exists := obj.Get("body")
   119  
   120  	if exists {
   121  		if core.IsTypeOf(body, types.Binary) {
   122  			p.Body = body.(values.Binary)
   123  		} else {
   124  			j, err := body.MarshalJSON()
   125  
   126  			if err != nil {
   127  				return Params{}, core.Error(err, ".body")
   128  			}
   129  
   130  			p.Body = values.NewBinary(j)
   131  
   132  			if p.Headers == nil {
   133  				p.Headers = values.NewObject()
   134  			}
   135  
   136  			p.Headers.Set("Content-Type", values.NewString("application/json"))
   137  		}
   138  	}
   139  
   140  	return p, nil
   141  }