github.com/pavlo67/common@v0.5.3/common/httplib/request.go (about)

     1  package httplib
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"regexp"
    11  
    12  	"github.com/pavlo67/common/common"
    13  	"github.com/pavlo67/common/common/errors"
    14  	"github.com/pavlo67/common/common/logger"
    15  	"github.com/pavlo67/common/common/server_http"
    16  )
    17  
    18  const bodyLogLimit = 2048
    19  
    20  const onRequest = "on httplib.Request()"
    21  
    22  type ResponseBinary struct {
    23  	MIMEType string
    24  	Data     []byte
    25  }
    26  
    27  var reBinary = regexp.MustCompile(`^image/`)
    28  
    29  func Request(client *http.Client, serverURL, method string, header http.Header, requestData, responseData interface{}, l logger.Operator, logFile string) error {
    30  	if client == nil {
    31  		client = &http.Client{}
    32  	}
    33  
    34  	var err error
    35  	//for _, doReAuth := range reAuthTries {
    36  
    37  	// start of single try
    38  
    39  	var requestBody []byte
    40  	var requestBodyReader io.Reader
    41  
    42  	if requestData != nil {
    43  		switch v := requestData.(type) {
    44  		case []byte:
    45  			requestBody = v
    46  		case *[]byte:
    47  			requestBody = *v
    48  		case string:
    49  			requestBody = []byte(v)
    50  		case *string:
    51  			requestBody = []byte(*v)
    52  		default:
    53  			if requestBody, err = json.Marshal(requestData); err != nil {
    54  				return fmt.Errorf(onRequest+": can't marshal request responseData (%#v): %s", requestData, err)
    55  			}
    56  		}
    57  
    58  		// must be checked for nil instead direct write
    59  		// the external for GET requests expected nil body, but nil-requestData after json.Marshal return not empty responseData
    60  
    61  		requestBodyReader = bytes.NewBuffer(requestBody)
    62  	}
    63  
    64  	req, err := http.NewRequest(method, serverURL, requestBodyReader)
    65  	if err != nil || req == nil {
    66  		logger.LogRequest(method, serverURL, nil, requestBody, nil, nil, err, 0, logFile)
    67  		return fmt.Errorf(onRequest+": can't create request %s %s, got %#v, %s", method, serverURL, req, err)
    68  	} else if req.Body != nil {
    69  		defer Close(req.Body, client, nil)
    70  	}
    71  	req.Header = header
    72  	var responseBody []byte
    73  
    74  	resp, err := client.Do(req)
    75  	if resp != nil && resp.Body != nil {
    76  		defer Close(resp.Body, client, nil)
    77  	}
    78  
    79  	if err != nil {
    80  		var statusCode int
    81  		var responseHeaders http.Header
    82  		if resp != nil {
    83  			statusCode = resp.StatusCode
    84  			responseHeaders = resp.Header
    85  			responseBody, _ = ioutil.ReadAll(resp.Body)
    86  		}
    87  
    88  		logger.LogRequest(method, serverURL, req.Header, requestBody, responseHeaders, responseBody, err, statusCode, logFile)
    89  		return fmt.Errorf(onRequest+": can't %s %s, got %s", method, serverURL, err)
    90  	}
    91  
    92  	responseBody, err = ioutil.ReadAll(resp.Body)
    93  	if err != nil {
    94  		l.Error("can't read response body: ", err)
    95  	}
    96  
    97  	var responseBodyToLog []byte
    98  
    99  	if !reBinary.MatchString(resp.Header.Get("Content-Type")) {
   100  		responseBodyToLog = responseBody
   101  	}
   102  	logger.LogRequest(method, serverURL, req.Header, requestBody, resp.Header, responseBodyToLog, err, resp.StatusCode, logFile)
   103  	if err != nil {
   104  		return fmt.Errorf(onRequest+": can't read body from %s %s, got %s", method, serverURL, err)
   105  	}
   106  
   107  	if resp.StatusCode == http.StatusUnauthorized { // && doReAuth
   108  		//if identity.Token = reAuthJWT(*identity); identity.Token != "" {
   109  		//	continue
   110  		//}
   111  	}
   112  
   113  	if resp.StatusCode != http.StatusOK {
   114  		// TODO!!! be careful writing server_http handlers, http.StatusOK is the only success code accepted here
   115  
   116  		if len(responseBody) > bodyLogLimit {
   117  			responseBody = responseBody[:bodyLogLimit]
   118  		}
   119  
   120  		var data common.Map
   121  		if err = json.Unmarshal(responseBody, &data); err != nil {
   122  			if len(responseBody) > bodyLogLimit {
   123  				responseBody = responseBody[:bodyLogLimit]
   124  			}
   125  			return fmt.Errorf(onRequest+": can't unmarshal body from %s %s: status = %d, body = %s, got %s", method, serverURL, resp.StatusCode, responseBody, err)
   126  		}
   127  
   128  		errCommon := fmt.Sprintf("can't %s %s: status = %d, body = %s", method, serverURL, resp.StatusCode, responseBody)
   129  		if data["error"] != nil {
   130  			data["error"] = errors.CommonError(data["error"], errCommon)
   131  		} else {
   132  			data["error"] = errCommon
   133  		}
   134  		errorKey := errors.Key(data.StringDefault(server_http.ErrorKey, ""))
   135  		return errors.CommonError(errorKey, data)
   136  	}
   137  
   138  	if responseData != nil {
   139  		switch v := responseData.(type) {
   140  		case *[]byte:
   141  			if v != nil {
   142  				*v = responseBody
   143  			}
   144  		case *string:
   145  			if v != nil {
   146  				*v = string(responseBody)
   147  			}
   148  		case *ResponseBinary:
   149  			v.MIMEType = resp.Header.Get("Content-Type")
   150  			v.Data = responseBody
   151  		default:
   152  			if err = json.Unmarshal(responseBody, responseData); err != nil {
   153  				if len(responseBody) > bodyLogLimit {
   154  					responseBody = responseBody[:bodyLogLimit]
   155  				}
   156  				return fmt.Errorf(onRequest+": can't unmarshal body from %s %s %s, got %s", method, serverURL, responseBody, err)
   157  			}
   158  		}
   159  	}
   160  
   161  	//	break // end of each try means the end of all tries if something other wasn't managed before
   162  	//}
   163  
   164  	return nil
   165  }
   166  
   167  //func RequestJSON(method, url string, data []byte, headers map[string]string) (common.Map, error) {
   168  //	client := &http.Client{}
   169  //
   170  //	req, err := http.NewRequest(method, url, bytes.NewReader(data))
   171  //	if err != nil {
   172  //		return nil, err
   173  //	}
   174  //
   175  //	for k, v := range headers {
   176  //		req.Header.Add(k, v)
   177  //	}
   178  //
   179  //	resp, err := client.Do(req)
   180  //	if err != nil {
   181  //		return nil, err
   182  //	}
   183  //
   184  //	defer resp.Body.Close()
   185  //	body, err := ioutil.ReadAll(resp.Body)
   186  //	if err != nil {
   187  //		return nil, err
   188  //	}
   189  //
   190  //	// log.Printf("%s", body)
   191  //
   192  //	result := common.Map{}
   193  //	err = json.Unmarshal(body, &result)
   194  //	if err != nil {
   195  //		return result, errors.Wrapf(err, "can't unmarsal: %s", body)
   196  //	}
   197  //
   198  //	return result, nil
   199  //}