github.com/webdestroya/awsmocker@v0.2.6/received_request.go (about)

     1  package awsmocker
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"regexp"
    10  	"strings"
    11  )
    12  
    13  var (
    14  	credExtractRegexp = regexp.MustCompile(`Credential=(\S+)\b`)
    15  	jsonRegexp        = regexp.MustCompile(`json`)
    16  )
    17  
    18  type ReceivedRequest struct {
    19  	HttpRequest *http.Request
    20  
    21  	Action  string
    22  	Service string
    23  	Region  string
    24  
    25  	Hostname string
    26  	Path     string
    27  
    28  	// The expected response type based upon the request. JSON requests answered with JSON,
    29  	// form param posts respond with XML
    30  	AssumedResponseType string
    31  
    32  	// This will only be populated if the request was NOT a form
    33  	RawBody []byte
    34  
    35  	// If the request was a JSON request, then this will be the parsed JSON
    36  	JsonPayload any
    37  
    38  	// TBA: maybe in the future we'll add invalid request flagging, for now allow all types
    39  	// invalid bool
    40  }
    41  
    42  func (rr *ReceivedRequest) Inspect() string {
    43  
    44  	if rr.Action != "" && rr.Service != "" {
    45  		return rr.Service + ":" + rr.Action
    46  	}
    47  
    48  	return fmt.Sprintf("%s %s/%s", rr.HttpRequest.Method, rr.Hostname, rr.Path)
    49  }
    50  
    51  func newReceivedRequest(req *http.Request) *ReceivedRequest {
    52  	recvreq := &ReceivedRequest{
    53  		HttpRequest:         req,
    54  		AssumedResponseType: ContentTypeText,
    55  		Hostname:            req.URL.Hostname(),
    56  		Path:                req.URL.Path,
    57  	}
    58  
    59  	_ = req.ParseForm()
    60  
    61  	bodyBytes, err := io.ReadAll(req.Body)
    62  	if err == nil {
    63  		recvreq.RawBody = bodyBytes
    64  	}
    65  
    66  	reqContentType := req.Header.Get("content-type")
    67  	if jsonRegexp.MatchString(reqContentType) {
    68  		recvreq.AssumedResponseType = ContentTypeJSON
    69  
    70  		if len(bodyBytes) > 0 {
    71  			var jsonData any
    72  			if err := json.Unmarshal(bodyBytes, &jsonData); err == nil {
    73  				recvreq.JsonPayload = jsonData
    74  			}
    75  		}
    76  
    77  	}
    78  
    79  	authHeader := req.Header.Get("authorization")
    80  	if authHeader != "" {
    81  		matches := credExtractRegexp.FindStringSubmatch(authHeader)
    82  		if len(matches) > 1 {
    83  			// 0       1         2      3    4
    84  			// fake/20221030/us-east-1/ecs/aws4_request
    85  			parts := strings.Split(matches[1], "/")
    86  			recvreq.Region = parts[2]
    87  			recvreq.Service = parts[3]
    88  		}
    89  	}
    90  
    91  	amzTarget := req.Header.Get("x-amz-target")
    92  	if amzTarget != "" {
    93  		// X-Amz-Target: AmazonEC2ContainerServiceV20141113.ListClusters
    94  		_, opname, ok := strings.Cut(amzTarget, ".")
    95  		if ok {
    96  			recvreq.Action = opname
    97  		}
    98  	}
    99  
   100  	if recvreq.Action == "" && req.PostForm.Has("Action") {
   101  		recvreq.Action = req.PostForm.Get("Action")
   102  	}
   103  
   104  	if recvreq.AssumedResponseType == ContentTypeText && recvreq.Action != "" && recvreq.Service != "" && reqContentType == "application/x-www-form-urlencoded" {
   105  		recvreq.AssumedResponseType = ContentTypeXML
   106  	}
   107  
   108  	// if recvreq.Action == "" {
   109  	// 	log.Println("WARN: Received a request with no action????")
   110  	// 	recvreq.invalid = true
   111  	// 	return recvreq
   112  	// }
   113  
   114  	return recvreq
   115  }
   116  
   117  func (r *ReceivedRequest) DebugDump() {
   118  	// var buf *bytes.Buffer
   119  	buf := new(bytes.Buffer)
   120  
   121  	fmt.Fprintln(buf, "--- AWSMOCKER RECEIVED REQUEST: -----------------------")
   122  
   123  	if r.Action != "" || r.Service != "" {
   124  		fmt.Fprintf(buf, "Operation: %s (service=%s @ %s)\n", r.Action, r.Service, r.Region)
   125  	}
   126  
   127  	fmt.Fprintf(buf, "%s %s\n", r.HttpRequest.Method, r.HttpRequest.RequestURI)
   128  	fmt.Fprintf(buf, "Host: %s\n", r.HttpRequest.Host)
   129  	for k, vlist := range r.HttpRequest.Header {
   130  		for _, v := range vlist {
   131  			fmt.Fprintf(buf, "%s: %s\n", k, v)
   132  		}
   133  	}
   134  
   135  	fmt.Fprintln(buf)
   136  
   137  	if len(r.RawBody) > 0 {
   138  		fmt.Fprintln(buf, "BODY:")
   139  		fmt.Fprintln(buf, string(r.RawBody))
   140  	} else if r.HttpRequest.Form != nil && len(r.HttpRequest.Form) > 0 {
   141  		fmt.Fprintln(buf, "PARAMS:")
   142  		for k, vlist := range r.HttpRequest.Form {
   143  			for _, v := range vlist {
   144  				fmt.Fprintf(buf, "  %s=%s\n", k, v)
   145  			}
   146  		}
   147  	}
   148  
   149  	fmt.Fprintln(buf, "-------------------------------------------------------")
   150  	fmt.Fprintln(buf)
   151  
   152  	_, _ = buf.WriteTo(DebugOutputWriter)
   153  }