github.com/MontFerret/ferret@v0.18.0/pkg/drivers/response.go (about) 1 package drivers 2 3 import ( 4 "context" 5 6 "github.com/wI2L/jettison" 7 8 "github.com/MontFerret/ferret/pkg/runtime/core" 9 "github.com/MontFerret/ferret/pkg/runtime/values" 10 "github.com/MontFerret/ferret/pkg/runtime/values/types" 11 ) 12 13 // HTTPResponse HTTP response object. 14 type ( 15 HTTPResponse struct { 16 URL string 17 StatusCode int 18 Status string 19 Headers *HTTPHeaders 20 Body []byte 21 ResponseTime float64 22 } 23 24 // responseMarshal is a structure that repeats HTTPResponse. It allows 25 // easily Marshal the HTTPResponse object. 26 responseMarshal struct { 27 URL string `json:"url"` 28 StatusCode int `json:"status_code"` 29 Status string `json:"status"` 30 Headers *HTTPHeaders `json:"headers"` 31 Body []byte `json:"body"` 32 ResponseTime float64 `json:"response_time"` 33 } 34 ) 35 36 func (resp *HTTPResponse) Type() core.Type { 37 return HTTPResponseType 38 } 39 40 func (resp *HTTPResponse) String() string { 41 return resp.Status 42 } 43 44 func (resp *HTTPResponse) Compare(other core.Value) int64 { 45 if other.Type() != HTTPResponseType { 46 return Compare(HTTPResponseType, other.Type()) 47 } 48 49 // this is a safe cast. Only *HTTPResponse implements core.Value. 50 // HTTPResponse does not. 51 otherResp := other.(*HTTPResponse) 52 53 comp := resp.Headers.Compare(otherResp.Headers) 54 if comp != 0 { 55 return comp 56 } 57 58 // it makes no sense to compare Status strings 59 // because they are always equal if StatusCode's are equal 60 return values.NewInt(resp.StatusCode). 61 Compare(values.NewInt(resp.StatusCode)) 62 } 63 64 func (resp *HTTPResponse) Unwrap() interface{} { 65 return resp 66 } 67 68 func (resp *HTTPResponse) Copy() core.Value { 69 cop := *resp 70 return &cop 71 } 72 73 func (resp *HTTPResponse) Hash() uint64 { 74 return values.Parse(resp).Hash() 75 } 76 77 func (resp *HTTPResponse) MarshalJSON() ([]byte, error) { 78 if resp == nil { 79 return values.None.MarshalJSON() 80 } 81 82 return jettison.MarshalOpts(responseMarshal(*resp), jettison.NoHTMLEscaping()) 83 } 84 85 func (resp *HTTPResponse) GetIn(ctx context.Context, path []core.Value) (core.Value, core.PathError) { 86 if len(path) == 0 { 87 return resp, nil 88 } 89 90 segmentIdx := 0 91 segment := path[segmentIdx] 92 93 if typ := segment.Type(); typ != types.String { 94 return values.None, core.NewPathError(core.TypeError(typ, types.String), segmentIdx) 95 } 96 97 field := segment.String() 98 99 switch field { 100 case "url", "URL": 101 return values.NewString(resp.URL), nil 102 case "status": 103 return values.NewString(resp.Status), nil 104 case "statusCode": 105 return values.NewInt(resp.StatusCode), nil 106 case "headers": 107 if len(path) == 1 { 108 return resp.Headers, nil 109 } 110 111 out, pathErr := resp.Headers.GetIn(ctx, path[1:]) 112 113 if pathErr != nil { 114 return values.None, core.NewPathErrorFrom(pathErr, segmentIdx) 115 } 116 117 return out, nil 118 case "body": 119 return values.NewBinary(resp.Body), nil 120 case "responseTime": 121 return values.NewFloat(resp.ResponseTime), nil 122 } 123 124 return values.None, nil 125 }